mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-01 15:30:06 +00:00
b334829a91
* Validate that if a construct contains a header and it's merge is reachable, the construct also contains the merge * updated block merging to not merge into the continue * update inlining to mark the original block of a single block loop as the continue * updated some tests * remove dead code * rename kBlockTypeHeader to kBlockTypeSelection for clarity
3172 lines
83 KiB
C++
3172 lines
83 KiB
C++
// Copyright (c) 2017 Valve Corporation
|
|
// Copyright (c) 2017 LunarG Inc.
|
|
//
|
|
// 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 <string>
|
|
|
|
#include "test/opt/pass_fixture.h"
|
|
#include "test/opt/pass_utils.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
namespace {
|
|
|
|
using DeadBranchElimTest = PassTest<::testing::Test>;
|
|
|
|
TEST_F(DeadBranchElimTest, IfThenElseTrue) {
|
|
// #version 140
|
|
//
|
|
// in vec4 BaseColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v;
|
|
// if (true)
|
|
// v = vec4(0.0,0.0,0.0,0.0);
|
|
// else
|
|
// v = vec4(1.0,1.0,1.0,1.0);
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
OpName %BaseColor "BaseColor"
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%float_0 = OpConstant %float 0
|
|
%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_1 = OpConstant %float 1
|
|
%16 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %7
|
|
%19 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
OpSelectionMerge %20 None
|
|
OpBranchConditional %true %21 %22
|
|
%21 = OpLabel
|
|
OpStore %v %14
|
|
OpBranch %20
|
|
%22 = OpLabel
|
|
OpStore %v %16
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %7
|
|
%19 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
OpStore %v %14
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, IfThenElseFalse) {
|
|
// #version 140
|
|
//
|
|
// in vec4 BaseColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v;
|
|
// if (false)
|
|
// v = vec4(0.0,0.0,0.0,0.0);
|
|
// else
|
|
// v = vec4(1.0,1.0,1.0,1.0);
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
OpName %BaseColor "BaseColor"
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%float_0 = OpConstant %float 0
|
|
%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_1 = OpConstant %float 1
|
|
%16 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %7
|
|
%19 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
OpSelectionMerge %20 None
|
|
OpBranchConditional %false %21 %22
|
|
%21 = OpLabel
|
|
OpStore %v %14
|
|
OpBranch %20
|
|
%22 = OpLabel
|
|
OpStore %v %16
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %7
|
|
%19 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
OpStore %v %16
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, IfThenTrue) {
|
|
// #version 140
|
|
//
|
|
// in vec4 BaseColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v = BaseColor;
|
|
// if (true)
|
|
// v = v * vec4(0.5,0.5,0.5,0.5);
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %BaseColor "BaseColor"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%float_0_5 = OpConstant %float 0.5
|
|
%15 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %7
|
|
%17 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%18 = OpLoad %v4float %BaseColor
|
|
OpStore %v %18
|
|
OpSelectionMerge %19 None
|
|
OpBranchConditional %true %20 %19
|
|
%20 = OpLabel
|
|
%21 = OpLoad %v4float %v
|
|
%22 = OpFMul %v4float %21 %15
|
|
OpStore %v %22
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %7
|
|
%17 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%18 = OpLoad %v4float %BaseColor
|
|
OpStore %v %18
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
%21 = OpLoad %v4float %v
|
|
%22 = OpFMul %v4float %21 %15
|
|
OpStore %v %22
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, IfThenFalse) {
|
|
// #version 140
|
|
//
|
|
// in vec4 BaseColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v = BaseColor;
|
|
// if (false)
|
|
// v = v * vec4(0.5,0.5,0.5,0.5);
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %BaseColor "BaseColor"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%float_0_5 = OpConstant %float 0.5
|
|
%15 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %7
|
|
%17 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%18 = OpLoad %v4float %BaseColor
|
|
OpStore %v %18
|
|
OpSelectionMerge %19 None
|
|
OpBranchConditional %false %20 %19
|
|
%20 = OpLabel
|
|
%21 = OpLoad %v4float %v
|
|
%22 = OpFMul %v4float %21 %15
|
|
OpStore %v %22
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %7
|
|
%17 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%18 = OpLoad %v4float %BaseColor
|
|
OpStore %v %18
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, IfThenElsePhiTrue) {
|
|
// Test handling of phi in merge block after dead branch elimination.
|
|
// Note: The SPIR-V has had store/load elimination and phi insertion
|
|
//
|
|
// #version 140
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v;
|
|
// if (true)
|
|
// v = vec4(0.0,0.0,0.0,0.0);
|
|
// else
|
|
// v = vec4(1.0,1.0,1.0,1.0);
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
%void = OpTypeVoid
|
|
%5 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%float_0 = OpConstant %float 0
|
|
%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_1 = OpConstant %float 1
|
|
%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %5
|
|
%17 = OpLabel
|
|
OpSelectionMerge %18 None
|
|
OpBranchConditional %true %19 %20
|
|
%19 = OpLabel
|
|
OpBranch %18
|
|
%20 = OpLabel
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
%21 = OpPhi %v4float %12 %19 %14 %20
|
|
OpStore %gl_FragColor %21
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %5
|
|
%17 = OpLabel
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
OpStore %gl_FragColor %12
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, IfThenElsePhiFalse) {
|
|
// Test handling of phi in merge block after dead branch elimination.
|
|
// Note: The SPIR-V has had store/load elimination and phi insertion
|
|
//
|
|
// #version 140
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v;
|
|
// if (true)
|
|
// v = vec4(0.0,0.0,0.0,0.0);
|
|
// else
|
|
// v = vec4(1.0,1.0,1.0,1.0);
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
%void = OpTypeVoid
|
|
%5 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%float_0 = OpConstant %float 0
|
|
%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_1 = OpConstant %float 1
|
|
%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %5
|
|
%17 = OpLabel
|
|
OpSelectionMerge %18 None
|
|
OpBranchConditional %false %19 %20
|
|
%19 = OpLabel
|
|
OpBranch %18
|
|
%20 = OpLabel
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
%21 = OpPhi %v4float %12 %19 %14 %20
|
|
OpStore %gl_FragColor %21
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %5
|
|
%17 = OpLabel
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
OpStore %gl_FragColor %14
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, CompoundIfThenElseFalse) {
|
|
// #version 140
|
|
//
|
|
// layout(std140) uniform U_t
|
|
// {
|
|
// bool g_B ;
|
|
// } ;
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v;
|
|
// if (false) {
|
|
// if (g_B)
|
|
// v = vec4(0.0,0.0,0.0,0.0);
|
|
// else
|
|
// v = vec4(1.0,1.0,1.0,1.0);
|
|
// } else {
|
|
// if (g_B)
|
|
// v = vec4(1.0,1.0,1.0,1.0);
|
|
// else
|
|
// v = vec4(0.0,0.0,0.0,0.0);
|
|
// }
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %U_t "U_t"
|
|
OpMemberName %U_t 0 "g_B"
|
|
OpName %_ ""
|
|
OpName %v "v"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
OpMemberDecorate %U_t 0 Offset 0
|
|
OpDecorate %U_t Block
|
|
OpDecorate %_ DescriptorSet 0
|
|
%void = OpTypeVoid
|
|
%8 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%uint = OpTypeInt 32 0
|
|
%U_t = OpTypeStruct %uint
|
|
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
|
|
%_ = OpVariable %_ptr_Uniform_U_t Uniform
|
|
%int = OpTypeInt 32 1
|
|
%int_0 = OpConstant %int 0
|
|
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
|
|
%uint_0 = OpConstant %uint 0
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%float_0 = OpConstant %float 0
|
|
%21 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_1 = OpConstant %float 1
|
|
%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %8
|
|
%25 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
OpSelectionMerge %26 None
|
|
OpBranchConditional %false %27 %28
|
|
%27 = OpLabel
|
|
%29 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
|
|
%30 = OpLoad %uint %29
|
|
%31 = OpINotEqual %bool %30 %uint_0
|
|
OpSelectionMerge %32 None
|
|
OpBranchConditional %31 %33 %34
|
|
%33 = OpLabel
|
|
OpStore %v %21
|
|
OpBranch %32
|
|
%34 = OpLabel
|
|
OpStore %v %23
|
|
OpBranch %32
|
|
%32 = OpLabel
|
|
OpBranch %26
|
|
%28 = OpLabel
|
|
%35 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
|
|
%36 = OpLoad %uint %35
|
|
%37 = OpINotEqual %bool %36 %uint_0
|
|
OpSelectionMerge %38 None
|
|
OpBranchConditional %37 %39 %40
|
|
%39 = OpLabel
|
|
OpStore %v %23
|
|
OpBranch %38
|
|
%40 = OpLabel
|
|
OpStore %v %21
|
|
OpBranch %38
|
|
%38 = OpLabel
|
|
OpBranch %26
|
|
%26 = OpLabel
|
|
%41 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %41
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %8
|
|
%25 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
OpBranch %28
|
|
%28 = OpLabel
|
|
%35 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
|
|
%36 = OpLoad %uint %35
|
|
%37 = OpINotEqual %bool %36 %uint_0
|
|
OpSelectionMerge %38 None
|
|
OpBranchConditional %37 %39 %40
|
|
%40 = OpLabel
|
|
OpStore %v %21
|
|
OpBranch %38
|
|
%39 = OpLabel
|
|
OpStore %v %23
|
|
OpBranch %38
|
|
%38 = OpLabel
|
|
OpBranch %26
|
|
%26 = OpLabel
|
|
%41 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %41
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, PreventOrphanMerge) {
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %BaseColor "BaseColor"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%float_0_5 = OpConstant %float 0.5
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %7
|
|
%16 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%17 = OpLoad %v4float %BaseColor
|
|
OpStore %v %17
|
|
OpSelectionMerge %18 None
|
|
OpBranchConditional %true %19 %20
|
|
%19 = OpLabel
|
|
OpKill
|
|
%20 = OpLabel
|
|
%21 = OpLoad %v4float %v
|
|
%22 = OpVectorTimesScalar %v4float %21 %float_0_5
|
|
OpStore %v %22
|
|
OpBranch %18
|
|
%18 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %7
|
|
%16 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%17 = OpLoad %v4float %BaseColor
|
|
OpStore %v %17
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
OpKill
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, HandleOrphanMerge) {
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %foo_ "foo("
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
OpDecorate %gl_FragColor Location 0
|
|
%void = OpTypeVoid
|
|
%6 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%9 = OpTypeFunction %v4float
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%float_0 = OpConstant %float 0
|
|
%13 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_1 = OpConstant %float 1
|
|
%15 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
%main = OpFunction %void None %6
|
|
%17 = OpLabel
|
|
%18 = OpFunctionCall %v4float %foo_
|
|
OpStore %gl_FragColor %18
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%foo_ = OpFunction %v4float None %9
|
|
%19 = OpLabel
|
|
OpSelectionMerge %20 None
|
|
OpBranchConditional %true %21 %22
|
|
%21 = OpLabel
|
|
OpReturnValue %13
|
|
%22 = OpLabel
|
|
OpReturnValue %15
|
|
%20 = OpLabel
|
|
%23 = OpUndef %v4float
|
|
OpReturnValue %23
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%foo_ = OpFunction %v4float None %9
|
|
%19 = OpLabel
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
OpReturnValue %13
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, KeepContinueTargetWhenKillAfterMerge) {
|
|
// #version 450
|
|
// void main() {
|
|
// bool c;
|
|
// bool d;
|
|
// while(c) {
|
|
// if(d) {
|
|
// continue;
|
|
// }
|
|
// if(false) {
|
|
// continue;
|
|
// }
|
|
// discard;
|
|
// }
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %c "c"
|
|
OpName %d "d"
|
|
%void = OpTypeVoid
|
|
%6 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%_ptr_Function_bool = OpTypePointer Function %bool
|
|
%false = OpConstantFalse %bool
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %6
|
|
%10 = OpLabel
|
|
%c = OpVariable %_ptr_Function_bool Function
|
|
%d = OpVariable %_ptr_Function_bool Function
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%15 = OpLoad %bool %c
|
|
OpBranchConditional %15 %16 %12
|
|
%16 = OpLabel
|
|
%17 = OpLoad %bool %d
|
|
OpSelectionMerge %18 None
|
|
OpBranchConditional %17 %19 %18
|
|
%19 = OpLabel
|
|
OpBranch %13
|
|
%18 = OpLabel
|
|
OpSelectionMerge %20 None
|
|
OpBranchConditional %false %21 %20
|
|
%21 = OpLabel
|
|
OpBranch %13
|
|
%20 = OpLabel
|
|
OpKill
|
|
%13 = OpLabel
|
|
OpBranch %11
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %6
|
|
%10 = OpLabel
|
|
%c = OpVariable %_ptr_Function_bool Function
|
|
%d = OpVariable %_ptr_Function_bool Function
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpLoopMerge %12 %13 None
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
%15 = OpLoad %bool %c
|
|
OpBranchConditional %15 %16 %12
|
|
%16 = OpLabel
|
|
%17 = OpLoad %bool %d
|
|
OpSelectionMerge %18 None
|
|
OpBranchConditional %17 %19 %18
|
|
%19 = OpLabel
|
|
OpBranch %13
|
|
%18 = OpLabel
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
OpKill
|
|
%13 = OpLabel
|
|
OpBranch %11
|
|
%12 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, DecorateDeleted) {
|
|
// Note: SPIR-V hand-edited to add decoration
|
|
// #version 140
|
|
//
|
|
// in vec4 BaseColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v = BaseColor;
|
|
// if (false)
|
|
// v = v * vec4(0.5,0.5,0.5,0.5);
|
|
// gl_FragColor = v;
|
|
// }
|
|
|
|
const std::string predefs_before =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %BaseColor "BaseColor"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
OpDecorate %22 RelaxedPrecision
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%float_0_5 = OpConstant %float 0.5
|
|
%15 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
)";
|
|
|
|
const std::string predefs_after =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %BaseColor "BaseColor"
|
|
OpName %gl_FragColor "gl_FragColor"
|
|
%void = OpTypeVoid
|
|
%8 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%float_0_5 = OpConstant %float 0.5
|
|
%16 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %7
|
|
%17 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%18 = OpLoad %v4float %BaseColor
|
|
OpStore %v %18
|
|
OpSelectionMerge %19 None
|
|
OpBranchConditional %false %20 %19
|
|
%20 = OpLabel
|
|
%21 = OpLoad %v4float %v
|
|
%22 = OpFMul %v4float %21 %15
|
|
OpStore %v %22
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %8
|
|
%18 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%19 = OpLoad %v4float %BaseColor
|
|
OpStore %v %19
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
%23 = OpLoad %v4float %v
|
|
OpStore %gl_FragColor %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs_before + before,
|
|
predefs_after + after, true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, LoopInDeadBranch) {
|
|
// #version 450
|
|
//
|
|
// layout(location = 0) in vec4 BaseColor;
|
|
// layout(location = 0) out vec4 OutColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// vec4 v = BaseColor;
|
|
// if (false)
|
|
// for (int i=0; i<3; i++)
|
|
// v = v * 0.5;
|
|
// OutColor = v;
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %BaseColor %OutColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %v "v"
|
|
OpName %BaseColor "BaseColor"
|
|
OpName %i "i"
|
|
OpName %OutColor "OutColor"
|
|
OpDecorate %BaseColor Location 0
|
|
OpDecorate %OutColor Location 0
|
|
%void = OpTypeVoid
|
|
%8 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%int_3 = OpConstant %int 3
|
|
%float_0_5 = OpConstant %float 0.5
|
|
%int_1 = OpConstant %int 1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%OutColor = OpVariable %_ptr_Output_v4float Output
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %8
|
|
%22 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%i = OpVariable %_ptr_Function_int Function
|
|
%23 = OpLoad %v4float %BaseColor
|
|
OpStore %v %23
|
|
OpSelectionMerge %24 None
|
|
OpBranchConditional %false %25 %24
|
|
%25 = OpLabel
|
|
OpStore %i %int_0
|
|
OpBranch %26
|
|
%26 = OpLabel
|
|
OpLoopMerge %27 %28 None
|
|
OpBranch %29
|
|
%29 = OpLabel
|
|
%30 = OpLoad %int %i
|
|
%31 = OpSLessThan %bool %30 %int_3
|
|
OpBranchConditional %31 %32 %27
|
|
%32 = OpLabel
|
|
%33 = OpLoad %v4float %v
|
|
%34 = OpVectorTimesScalar %v4float %33 %float_0_5
|
|
OpStore %v %34
|
|
OpBranch %28
|
|
%28 = OpLabel
|
|
%35 = OpLoad %int %i
|
|
%36 = OpIAdd %int %35 %int_1
|
|
OpStore %i %36
|
|
OpBranch %26
|
|
%27 = OpLabel
|
|
OpBranch %24
|
|
%24 = OpLabel
|
|
%37 = OpLoad %v4float %v
|
|
OpStore %OutColor %37
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %8
|
|
%22 = OpLabel
|
|
%v = OpVariable %_ptr_Function_v4float Function
|
|
%i = OpVariable %_ptr_Function_int Function
|
|
%23 = OpLoad %v4float %BaseColor
|
|
OpStore %v %23
|
|
OpBranch %24
|
|
%24 = OpLabel
|
|
%37 = OpLoad %v4float %v
|
|
OpStore %OutColor %37
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SwitchLiveCase) {
|
|
// #version 450
|
|
//
|
|
// layout (location=0) in vec4 BaseColor;
|
|
// layout (location=0) out vec4 OutColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// switch (1) {
|
|
// case 0:
|
|
// OutColor = vec4(0.0,0.0,0.0,0.0);
|
|
// break;
|
|
// case 1:
|
|
// OutColor = vec4(0.125,0.125,0.125,0.125);
|
|
// break;
|
|
// case 2:
|
|
// OutColor = vec4(0.25,0.25,0.25,0.25);
|
|
// break;
|
|
// default:
|
|
// OutColor = vec4(1.0,1.0,1.0,1.0);
|
|
// }
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %OutColor %BaseColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %OutColor "OutColor"
|
|
OpName %BaseColor "BaseColor"
|
|
OpDecorate %OutColor Location 0
|
|
OpDecorate %BaseColor Location 0
|
|
%void = OpTypeVoid
|
|
%6 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%int_1 = OpConstant %int 1
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%OutColor = OpVariable %_ptr_Output_v4float Output
|
|
%float_0 = OpConstant %float 0
|
|
%13 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_0_125 = OpConstant %float 0.125
|
|
%15 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125
|
|
%float_0_25 = OpConstant %float 0.25
|
|
%17 = OpConstantComposite %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25
|
|
%float_1 = OpConstant %float 1
|
|
%19 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %6
|
|
%21 = OpLabel
|
|
OpSelectionMerge %22 None
|
|
OpSwitch %int_1 %23 0 %24 1 %25 2 %26
|
|
%23 = OpLabel
|
|
OpStore %OutColor %19
|
|
OpBranch %22
|
|
%24 = OpLabel
|
|
OpStore %OutColor %13
|
|
OpBranch %22
|
|
%25 = OpLabel
|
|
OpStore %OutColor %15
|
|
OpBranch %22
|
|
%26 = OpLabel
|
|
OpStore %OutColor %17
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %6
|
|
%21 = OpLabel
|
|
OpBranch %25
|
|
%25 = OpLabel
|
|
OpStore %OutColor %15
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SwitchLiveDefault) {
|
|
// #version 450
|
|
//
|
|
// layout (location=0) in vec4 BaseColor;
|
|
// layout (location=0) out vec4 OutColor;
|
|
//
|
|
// void main()
|
|
// {
|
|
// switch (7) {
|
|
// case 0:
|
|
// OutColor = vec4(0.0,0.0,0.0,0.0);
|
|
// break;
|
|
// case 1:
|
|
// OutColor = vec4(0.125,0.125,0.125,0.125);
|
|
// break;
|
|
// case 2:
|
|
// OutColor = vec4(0.25,0.25,0.25,0.25);
|
|
// break;
|
|
// default:
|
|
// OutColor = vec4(1.0,1.0,1.0,1.0);
|
|
// }
|
|
// }
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %OutColor %BaseColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %OutColor "OutColor"
|
|
OpName %BaseColor "BaseColor"
|
|
OpDecorate %OutColor Location 0
|
|
OpDecorate %BaseColor Location 0
|
|
%void = OpTypeVoid
|
|
%6 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%int_7 = OpConstant %int 7
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%OutColor = OpVariable %_ptr_Output_v4float Output
|
|
%float_0 = OpConstant %float 0
|
|
%13 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_0_125 = OpConstant %float 0.125
|
|
%15 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125
|
|
%float_0_25 = OpConstant %float 0.25
|
|
%17 = OpConstantComposite %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25
|
|
%float_1 = OpConstant %float 1
|
|
%19 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %6
|
|
%21 = OpLabel
|
|
OpSelectionMerge %22 None
|
|
OpSwitch %int_7 %23 0 %24 1 %25 2 %26
|
|
%23 = OpLabel
|
|
OpStore %OutColor %19
|
|
OpBranch %22
|
|
%24 = OpLabel
|
|
OpStore %OutColor %13
|
|
OpBranch %22
|
|
%25 = OpLabel
|
|
OpStore %OutColor %15
|
|
OpBranch %22
|
|
%26 = OpLabel
|
|
OpStore %OutColor %17
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %6
|
|
%21 = OpLabel
|
|
OpBranch %23
|
|
%23 = OpLabel
|
|
OpStore %OutColor %19
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SwitchLiveCaseBreakFromLoop) {
|
|
// This sample does not directly translate to GLSL/HLSL as
|
|
// direct breaks from a loop cannot be made from a switch.
|
|
// This construct is currently formed by inlining a function
|
|
// containing early returns from the cases of a switch. The
|
|
// function is wrapped in a one-trip loop and returns are
|
|
// translated to branches to the loop's merge block.
|
|
|
|
const std::string predefs =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %OutColor %BaseColor
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %oc "oc"
|
|
OpName %OutColor "OutColor"
|
|
OpName %BaseColor "BaseColor"
|
|
OpDecorate %OutColor Location 0
|
|
OpDecorate %BaseColor Location 0
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%false = OpConstantFalse %bool
|
|
%int = OpTypeInt 32 1
|
|
%int_1 = OpConstant %int 1
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
|
%float_0 = OpConstant %float 0
|
|
%17 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
|
|
%float_0_125 = OpConstant %float 0.125
|
|
%19 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125
|
|
%float_0_25 = OpConstant %float 0.25
|
|
%21 = OpConstantComposite %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25
|
|
%float_1 = OpConstant %float 1
|
|
%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%OutColor = OpVariable %_ptr_Output_v4float Output
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%BaseColor = OpVariable %_ptr_Input_v4float Input
|
|
)";
|
|
|
|
const std::string before =
|
|
R"(%main = OpFunction %void None %7
|
|
%26 = OpLabel
|
|
%oc = OpVariable %_ptr_Function_v4float Function
|
|
OpBranch %27
|
|
%27 = OpLabel
|
|
OpLoopMerge %28 %29 None
|
|
OpBranch %30
|
|
%30 = OpLabel
|
|
OpSelectionMerge %31 None
|
|
OpSwitch %int_1 %31 0 %32 1 %33 2 %34
|
|
%32 = OpLabel
|
|
OpStore %oc %17
|
|
OpBranch %28
|
|
%33 = OpLabel
|
|
OpStore %oc %19
|
|
OpBranch %28
|
|
%34 = OpLabel
|
|
OpStore %oc %21
|
|
OpBranch %28
|
|
%31 = OpLabel
|
|
OpStore %oc %23
|
|
OpBranch %28
|
|
%29 = OpLabel
|
|
OpBranchConditional %false %27 %28
|
|
%28 = OpLabel
|
|
%35 = OpLoad %v4float %oc
|
|
OpStore %OutColor %35
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(%main = OpFunction %void None %7
|
|
%26 = OpLabel
|
|
%oc = OpVariable %_ptr_Function_v4float Function
|
|
OpBranch %27
|
|
%27 = OpLabel
|
|
OpLoopMerge %28 %29 None
|
|
OpBranch %30
|
|
%30 = OpLabel
|
|
OpBranch %33
|
|
%33 = OpLabel
|
|
OpStore %oc %19
|
|
OpBranch %28
|
|
%29 = OpLabel
|
|
OpBranch %27
|
|
%28 = OpLabel
|
|
%35 = OpLoad %v4float %oc
|
|
OpStore %OutColor %35
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(predefs + before, predefs + after,
|
|
true, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, LeaveContinueBackedge) {
|
|
const std::string text = R"(
|
|
; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None
|
|
; CHECK: [[continue]] = OpLabel
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} {{%\w+}} [[merge]]
|
|
; CHECK-NEXT: [[merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%void = OpTypeVoid
|
|
%funcTy = OpTypeFunction %void
|
|
%func = OpFunction %void None %funcTy
|
|
%1 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
OpLoopMerge %3 %4 None
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
; Be careful we don't remove the backedge to %2 despite never taking it.
|
|
OpBranchConditional %false %2 %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
TEST_F(DeadBranchElimTest, LeaveContinueBackedgeExtraBlock) {
|
|
const std::string text = R"(
|
|
; CHECK: OpBranch [[header:%\w+]]
|
|
; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None
|
|
; CHECK-NEXT: OpBranch [[continue]]
|
|
; CHECK-NEXT: [[continue]] = OpLabel
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[extra:%\w+]] [[merge]]
|
|
; CHECK-NEXT: [[extra]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header]]
|
|
; CHECK-NEXT: [[merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%void = OpTypeVoid
|
|
%funcTy = OpTypeFunction %void
|
|
%func = OpFunction %void None %funcTy
|
|
%1 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
OpLoopMerge %3 %4 None
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
; Be careful we don't remove the backedge to %2 despite never taking it.
|
|
OpBranchConditional %false %5 %3
|
|
; This block remains live despite being unreachable.
|
|
%5 = OpLabel
|
|
OpBranch %2
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, RemovePhiWithUnreachableContinue) {
|
|
const std::string text = R"(
|
|
; CHECK: [[entry:%\w+]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header:%\w+]]
|
|
; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None
|
|
; CHECK-NEXT: OpBranch [[ret:%\w+]]
|
|
; CHECK-NEXT: [[ret]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
; CHECK: [[continue]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header]]
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK-NEXT: OpUnreachable
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%true = OpConstantTrue %bool
|
|
%void = OpTypeVoid
|
|
%funcTy = OpTypeFunction %void
|
|
%func = OpFunction %void None %funcTy
|
|
%1 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%phi = OpPhi %bool %false %1 %true %continue
|
|
OpLoopMerge %merge %continue None
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
%continue = OpLabel
|
|
OpBranch %2
|
|
%merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, UnreachableLoopMergeAndContinueTargets) {
|
|
const std::string text = R"(
|
|
; CHECK: [[undef:%\w+]] = OpUndef %bool
|
|
; CHECK: OpSelectionMerge [[header:%\w+]]
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_lab:%\w+]] [[else_lab:%\w+]]
|
|
; CHECK: OpPhi %bool %false [[if_lab]] %false [[else_lab]] [[undef]] [[continue:%\w+]]
|
|
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None
|
|
; CHECK-NEXT: OpBranch [[ret:%\w+]]
|
|
; CHECK-NEXT: [[ret]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
; CHECK: [[continue]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header]]
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK-NEXT: OpUnreachable
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%true = OpConstantTrue %bool
|
|
%void = OpTypeVoid
|
|
%funcTy = OpTypeFunction %void
|
|
%func = OpFunction %void None %funcTy
|
|
%1 = OpLabel
|
|
%c = OpUndef %bool
|
|
OpSelectionMerge %2 None
|
|
OpBranchConditional %c %if %else
|
|
%if = OpLabel
|
|
OpBranch %2
|
|
%else = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%phi = OpPhi %bool %false %if %false %else %true %continue
|
|
OpLoopMerge %merge %continue None
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
%continue = OpLabel
|
|
OpBranch %2
|
|
%merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
TEST_F(DeadBranchElimTest, EarlyReconvergence) {
|
|
const std::string text = R"(
|
|
; CHECK-NOT: OpBranchConditional
|
|
; CHECK: [[logical:%\w+]] = OpLogicalOr
|
|
; CHECK-NOT: OpPhi
|
|
; CHECK: OpLogicalAnd {{%\w+}} {{%\w+}} [[logical]]
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%true = OpConstantTrue %bool
|
|
%func_ty = OpTypeFunction %void
|
|
%func = OpFunction %void None %func_ty
|
|
%1 = OpLabel
|
|
OpSelectionMerge %2 None
|
|
OpBranchConditional %false %3 %4
|
|
%3 = OpLabel
|
|
%12 = OpLogicalNot %bool %true
|
|
OpBranch %2
|
|
%4 = OpLabel
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %false %5 %6
|
|
%5 = OpLabel
|
|
%10 = OpLogicalAnd %bool %true %false
|
|
OpBranch %7
|
|
%6 = OpLabel
|
|
%11 = OpLogicalOr %bool %true %false
|
|
OpBranch %7
|
|
%7 = OpLabel
|
|
; This phi is in a block preceeding the merge %14!
|
|
%8 = OpPhi %bool %10 %5 %11 %6
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
%9 = OpPhi %bool %12 %3 %8 %14
|
|
%13 = OpLogicalAnd %bool %true %9
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksFloating) {
|
|
const std::string text = R"(
|
|
; CHECK: OpFunction
|
|
; CHECK-NEXT: OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
; CHECK-NEXT: OpFunctionEnd
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%1 = OpTypeFunction %void
|
|
%func = OpFunction %void None %1
|
|
%2 = OpLabel
|
|
OpReturn
|
|
%3 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksFloatingJoin) {
|
|
const std::string text = R"(
|
|
; CHECK: OpFunction
|
|
; CHECK-NEXT: OpFunctionParameter
|
|
; CHECK-NEXT: OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
; CHECK-NEXT: OpFunctionEnd
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpTypeFunction %void %bool
|
|
%func = OpFunction %void None %1
|
|
%bool_param = OpFunctionParameter %bool
|
|
%2 = OpLabel
|
|
OpReturn
|
|
%3 = OpLabel
|
|
OpSelectionMerge %6 None
|
|
OpBranchConditional %bool_param %4 %5
|
|
%4 = OpLabel
|
|
OpBranch %6
|
|
%5 = OpLabel
|
|
OpBranch %6
|
|
%6 = OpLabel
|
|
%7 = OpPhi %bool %true %4 %false %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksDeadPhi) {
|
|
const std::string text = R"(
|
|
; CHECK: OpFunction
|
|
; CHECK-NEXT: OpFunctionParameter
|
|
; CHECK-NEXT: OpLabel
|
|
; CHECK-NEXT: OpBranch [[label:%\w+]]
|
|
; CHECK-NEXT: [[label]] = OpLabel
|
|
; CHECK-NEXT: OpLogicalNot %bool %true
|
|
; CHECK-NEXT: OpReturn
|
|
; CHECK-NEXT: OpFunctionEnd
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpTypeFunction %void %bool
|
|
%func = OpFunction %void None %1
|
|
%bool_param = OpFunctionParameter %bool
|
|
%2 = OpLabel
|
|
OpBranch %3
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
%5 = OpPhi %bool %true %2 %false %4
|
|
%6 = OpLogicalNot %bool %5
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksPartiallyDeadPhi) {
|
|
const std::string text = R"(
|
|
; CHECK: OpFunction
|
|
; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter
|
|
; CHECK-NEXT: OpLabel
|
|
; CHECK-NEXT: OpBranchConditional [[param]] [[merge:%\w+]] [[br:%\w+]]
|
|
; CHECK-NEXT: [[merge]] = OpLabel
|
|
; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool %true %2 %false [[br]]
|
|
; CHECK-NEXT: OpLogicalNot %bool [[phi]]
|
|
; CHECK-NEXT: OpReturn
|
|
; CHECK-NEXT: [[br]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[merge]]
|
|
; CHECK-NEXT: OpFunctionEnd
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%true = OpConstantTrue %bool
|
|
%1 = OpTypeFunction %void %bool
|
|
%func = OpFunction %void None %1
|
|
%bool_param = OpFunctionParameter %bool
|
|
%2 = OpLabel
|
|
OpBranchConditional %bool_param %3 %7
|
|
%7 = OpLabel
|
|
OpBranch %3
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
%5 = OpPhi %bool %true %2 %false %7 %false %4
|
|
%6 = OpLogicalNot %bool %5
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, LiveHeaderDeadPhi) {
|
|
const std::string text = R"(
|
|
; CHECK: OpLabel
|
|
; CHECK-NOT: OpBranchConditional
|
|
; CHECK-NOT: OpPhi
|
|
; CHECK: OpLogicalNot %bool %false
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%false = OpConstantFalse %bool
|
|
%func_ty = OpTypeFunction %void
|
|
%func = OpFunction %void None %func_ty
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %true %2 %3
|
|
%2 = OpLabel
|
|
OpBranch %3
|
|
%3 = OpLabel
|
|
%5 = OpPhi %bool %true %3 %false %2
|
|
%6 = OpLogicalNot %bool %5
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, ExtraBackedgeBlocksLive) {
|
|
const std::string text = R"(
|
|
; CHECK: [[entry:%\w+]] = OpLabel
|
|
; CHECK-NOT: OpSelectionMerge
|
|
; CHECK: OpBranch [[header:%\w+]]
|
|
; CHECK-NEXT: [[header]] = OpLabel
|
|
; CHECK-NEXT: OpPhi %bool %true [[entry]] %false [[backedge:%\w+]]
|
|
; CHECK-NEXT: OpLoopMerge
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%false = OpConstantFalse %bool
|
|
%func_ty = OpTypeFunction %void %bool
|
|
%func = OpFunction %void None %func_ty
|
|
%param = OpFunctionParameter %bool
|
|
%entry = OpLabel
|
|
OpSelectionMerge %if_merge None
|
|
; This dead branch is included to ensure the pass does work.
|
|
OpBranchConditional %false %if_merge %loop_header
|
|
%loop_header = OpLabel
|
|
; Both incoming edges are live, so the phi should be untouched.
|
|
%phi = OpPhi %bool %true %entry %false %backedge
|
|
OpLoopMerge %loop_merge %continue None
|
|
OpBranchConditional %param %loop_merge %continue
|
|
%continue = OpLabel
|
|
OpBranch %backedge
|
|
%backedge = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpBranch %if_merge
|
|
%if_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, ExtraBackedgeBlocksUnreachable) {
|
|
const std::string text = R"(
|
|
; CHECK: [[entry:%\w+]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header:%\w+]]
|
|
; CHECK-NEXT: [[header]] = OpLabel
|
|
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None
|
|
; CHECK-NEXT: OpBranch [[merge]]
|
|
; CHECK-NEXT: [[merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
; CHECK-NEXT: [[continue]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header]]
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%false = OpConstantFalse %bool
|
|
%func_ty = OpTypeFunction %void %bool
|
|
%func = OpFunction %void None %func_ty
|
|
%param = OpFunctionParameter %bool
|
|
%entry = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
; Since the continue is unreachable, %backedge will be removed. The phi will
|
|
; instead require an edge from %continue.
|
|
%phi = OpPhi %bool %true %entry %false %backedge
|
|
OpLoopMerge %merge %continue None
|
|
OpBranch %merge
|
|
%continue = OpLabel
|
|
OpBranch %backedge
|
|
%backedge = OpLabel
|
|
OpBranch %loop_header
|
|
%merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, NoUnnecessaryChanges) {
|
|
const std::string text = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%undef = OpUndef %bool
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%1 = OpLabel
|
|
OpBranch %2
|
|
%2 = OpLabel
|
|
OpLoopMerge %4 %5 None
|
|
OpBranch %6
|
|
%6 = OpLabel
|
|
OpReturn
|
|
%5 = OpLabel
|
|
OpBranch %2
|
|
%4 = OpLabel
|
|
OpUnreachable
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
auto result = SinglePassRunToBinary<DeadBranchElimPass>(text, true);
|
|
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, ExtraBackedgePartiallyDead) {
|
|
const std::string text = R"(
|
|
; CHECK: OpLabel
|
|
; CHECK: [[header:%\w+]] = OpLabel
|
|
; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None
|
|
; CHECK: [[merge]] = OpLabel
|
|
; CHECK: [[continue]] = OpLabel
|
|
; CHECK: OpBranch [[extra:%\w+]]
|
|
; CHECK: [[extra]] = OpLabel
|
|
; CHECK-NOT: OpSelectionMerge
|
|
; CHECK-NEXT: OpBranch [[else:%\w+]]
|
|
; CHECK-NEXT: [[else]] = OpLabel
|
|
; CHECK-NEXT: OpLogicalOr
|
|
; CHECK-NEXT: OpBranch [[backedge:%\w+]]
|
|
; CHECK-NEXT: [[backedge:%\w+]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header]]
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpName %func "func"
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%false = OpConstantFalse %bool
|
|
%func_ty = OpTypeFunction %void %bool
|
|
%func = OpFunction %void None %func_ty
|
|
%param = OpFunctionParameter %bool
|
|
%entry = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %continue None
|
|
OpBranchConditional %param %loop_merge %continue
|
|
%continue = OpLabel
|
|
OpBranch %extra
|
|
%extra = OpLabel
|
|
OpSelectionMerge %backedge None
|
|
OpBranchConditional %false %then %else
|
|
%then = OpLabel
|
|
%and = OpLogicalAnd %bool %true %false
|
|
OpBranch %backedge
|
|
%else = OpLabel
|
|
%or = OpLogicalOr %bool %true %false
|
|
OpBranch %backedge
|
|
%backedge = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, UnreachableContinuePhiInMerge) {
|
|
const std::string text = R"(
|
|
; CHECK: [[entry:%\w+]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header:%\w+]]
|
|
; CHECK-NEXT: [[header]] = OpLabel
|
|
; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None
|
|
; CHECK-NEXT: OpBranch [[label:%\w+]]
|
|
; CHECK-NEXT: [[label]] = OpLabel
|
|
; CHECK-NEXT: [[fadd:%\w+]] = OpFAdd
|
|
; CHECK-NEXT: OpBranch [[label:%\w+]]
|
|
; CHECK-NEXT: [[label]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[merge]]
|
|
; CHECK-NEXT: [[continue]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[header]]
|
|
; CHECK-NEXT: [[merge]] = OpLabel
|
|
; CHECK-NEXT: OpStore {{%\w+}} [[fadd]]
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %o
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 430
|
|
OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
|
|
OpSourceExtension "GL_GOOGLE_include_directive"
|
|
OpName %main "main"
|
|
OpName %o "o"
|
|
OpName %S "S"
|
|
OpMemberName %S 0 "a"
|
|
OpName %U_t "U_t"
|
|
OpMemberName %U_t 0 "g_F"
|
|
OpMemberName %U_t 1 "g_F2"
|
|
OpDecorate %o Location 0
|
|
OpMemberDecorate %S 0 Offset 0
|
|
OpMemberDecorate %U_t 0 Volatile
|
|
OpMemberDecorate %U_t 0 Offset 0
|
|
OpMemberDecorate %U_t 1 Offset 4
|
|
OpDecorate %U_t BufferBlock
|
|
%void = OpTypeVoid
|
|
%7 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%_ptr_Function_float = OpTypePointer Function %float
|
|
%float_0 = OpConstant %float 0
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_0 = OpConstant %int 0
|
|
%int_10 = OpConstant %int 10
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%float_1 = OpConstant %float 1
|
|
%float_5 = OpConstant %float 5
|
|
%int_1 = OpConstant %int 1
|
|
%_ptr_Output_float = OpTypePointer Output %float
|
|
%o = OpVariable %_ptr_Output_float Output
|
|
%S = OpTypeStruct %float
|
|
%U_t = OpTypeStruct %S %S
|
|
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
|
|
%main = OpFunction %void None %7
|
|
%22 = OpLabel
|
|
OpBranch %23
|
|
%23 = OpLabel
|
|
%24 = OpPhi %float %float_0 %22 %25 %26
|
|
%27 = OpPhi %int %int_0 %22 %28 %26
|
|
OpLoopMerge %29 %26 None
|
|
OpBranch %40
|
|
%40 = OpLabel
|
|
%25 = OpFAdd %float %24 %float_1
|
|
OpSelectionMerge %30 None
|
|
OpBranchConditional %true %31 %30
|
|
%31 = OpLabel
|
|
OpBranch %29
|
|
%30 = OpLabel
|
|
OpBranch %26
|
|
%26 = OpLabel
|
|
%28 = OpIAdd %int %27 %int_1
|
|
%32 = OpSLessThan %bool %27 %int_10
|
|
; continue block branches to the header or another none dead block.
|
|
OpBranchConditional %32 %23 %29
|
|
%29 = OpLabel
|
|
%33 = OpPhi %float %24 %26 %25 %31
|
|
OpStore %o %33
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, NonStructuredIf) {
|
|
const std::string text = R"(
|
|
; CHECK-NOT: OpBranchConditional
|
|
OpCapability Kernel
|
|
OpCapability Linkage
|
|
OpMemoryModel Logical OpenCL
|
|
OpDecorate %func LinkageAttributes "func" Export
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%functy = OpTypeFunction %void
|
|
%func = OpFunction %void None %functy
|
|
%entry = OpLabel
|
|
OpBranchConditional %true %then %else
|
|
%then = OpLabel
|
|
OpBranch %final
|
|
%else = OpLabel
|
|
OpBranch %final
|
|
%final = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, ReorderBlocks) {
|
|
const std::string text = R"(
|
|
; CHECK: OpLabel
|
|
; CHECK: OpBranch [[label:%\w+]]
|
|
; CHECK: [[label:%\w+]] = OpLabel
|
|
; CHECK-NEXT: OpLogicalNot
|
|
; CHECK-NEXT: OpBranch [[label:%\w+]]
|
|
; CHECK: [[label]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%func_ty = OpTypeFunction %void
|
|
%func = OpFunction %void None %func_ty
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %true %2 %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
%2 = OpLabel
|
|
%not = OpLogicalNot %bool %true
|
|
OpBranch %3
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, ReorderBlocksMultiple) {
|
|
// Checks are not important. The validation post optimization is the
|
|
// important part.
|
|
const std::string text = R"(
|
|
; CHECK: OpLabel
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%func_ty = OpTypeFunction %void
|
|
%func = OpFunction %void None %func_ty
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %true %2 %3
|
|
%3 = OpLabel
|
|
OpReturn
|
|
%2 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, ReorderBlocksMultiple2) {
|
|
// Checks are not important. The validation post optimization is the
|
|
// important part.
|
|
const std::string text = R"(
|
|
; CHECK: OpLabel
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %func "func"
|
|
OpExecutionMode %func OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%func_ty = OpTypeFunction %void
|
|
%func = OpFunction %void None %func_ty
|
|
%1 = OpLabel
|
|
OpSelectionMerge %3 None
|
|
OpBranchConditional %true %2 %3
|
|
%3 = OpLabel
|
|
OpBranch %5
|
|
%5 = OpLabel
|
|
OpReturn
|
|
%2 = OpLabel
|
|
OpBranch %4
|
|
%4 = OpLabel
|
|
OpBranch %3
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(text, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithEarlyExit1) {
|
|
// Checks that if a selection merge construct contains a conditional branch
|
|
// to the merge node, then the OpSelectionMerge instruction is positioned
|
|
// correctly.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%undef_bool = OpUndef %bool
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpFunction
|
|
; CHECK-NEXT: OpLabel
|
|
; CHECK-NEXT: OpBranch [[taken_branch:%\w+]]
|
|
; CHECK-NEXT: [[taken_branch]] = OpLabel
|
|
; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]]
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[merge]] {{%\w+}}
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpSelectionMerge %outer_merge None
|
|
OpBranchConditional %true %bb1 %bb3
|
|
%bb1 = OpLabel
|
|
OpBranchConditional %undef_bool %outer_merge %bb2
|
|
%bb2 = OpLabel
|
|
OpBranch %outer_merge
|
|
%bb3 = OpLabel
|
|
OpBranch %outer_merge
|
|
%outer_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithEarlyExit2) {
|
|
// Checks that if a selection merge construct contains a conditional branch
|
|
// to the merge node, then the OpSelectionMerge instruction is positioned
|
|
// correctly.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%undef_bool = OpUndef %bool
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpFunction
|
|
; CHECK-NEXT: OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK-NEXT: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpSelectionMerge [[inner_merge:%\w+]]
|
|
; CHECK: [[inner_merge]] = OpLabel
|
|
; CHECK-NEXT: OpSelectionMerge [[outer_merge:%\w+]]
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[outer_merge]:%\w+]] {{%\w+}}
|
|
; CHECK: [[outer_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpSelectionMerge %outer_merge None
|
|
OpBranchConditional %true %bb1 %bb5
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %inner_merge None
|
|
OpBranchConditional %undef_bool %bb2 %bb3
|
|
%bb2 = OpLabel
|
|
OpBranch %inner_merge
|
|
%bb3 = OpLabel
|
|
OpBranch %inner_merge
|
|
%inner_merge = OpLabel
|
|
OpBranchConditional %undef_bool %outer_merge %bb4
|
|
%bb4 = OpLabel
|
|
OpBranch %outer_merge
|
|
%bb5 = OpLabel
|
|
OpBranch %outer_merge
|
|
%outer_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithConditionalExit) {
|
|
// Checks that if a selection merge construct contains a conditional branch
|
|
// to the merge node, then we keep the OpSelectionMerge on that branch.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%undef_int = OpUndef %uint
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
|
|
; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 1 [[bb3:%\w+]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[sel_merge]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpSwitch %undef_int %sel_merge 1 %bb3
|
|
%bb3 = OpLabel
|
|
OpBranch %sel_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop) {
|
|
// Checks that if a selection merge construct contains a conditional branch
|
|
// to a loop surrounding the selection merge, then we do not keep the
|
|
// OpSelectionMerge instruction.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%undef_bool = OpUndef %bool
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb3:%\w+]] [[loop_merge]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpBranchConditional %undef_bool %bb3 %loop_merge
|
|
%bb3 = OpLabel
|
|
OpBranch %sel_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue) {
|
|
// Checks that if a selection merge construct contains a conditional branch
|
|
// to continue of a loop surrounding the selection merge, then we do not keep
|
|
// the OpSelectionMerge instruction.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%undef_bool = OpUndef %bool
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(;
|
|
; CHECK: OpLabel
|
|
; CHECK: [[loop_header:%\w+]] = OpLabel
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb3:%\w+]] [[loop_cont]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_cont]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_header]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpBranchConditional %undef_bool %bb3 %cont
|
|
%bb3 = OpLabel
|
|
OpBranch %sel_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop2) {
|
|
// Same as |SelectionMergeWithExitToLoop|, except the switch goes to the loop
|
|
// merge or the selection merge. In this case, we do not need an
|
|
// OpSelectionMerge either.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%undef_bool = OpUndef %bool
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge:%\w+]] [[loop_merge]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpBranchConditional %undef_bool %sel_merge %loop_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue2) {
|
|
// Same as |SelectionMergeWithExitToLoopContinue|, except the branch goes to
|
|
// the loop continue or the selection merge. In this case, we do not need an
|
|
// OpSelectionMerge either.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%undef_bool = OpUndef %bool
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLabel
|
|
; CHECK: [[loop_header:%\w+]] = OpLabel
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge:%\w+]] [[loop_cont]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_cont]] = OpLabel
|
|
; CHECK: OpBranch [[loop_header]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpBranchConditional %undef_bool %sel_merge %cont
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop3) {
|
|
// Checks that if a selection merge construct contains a conditional branch
|
|
// to the merge of a surrounding loop, the selection merge, and another block
|
|
// inside the selection merge, then we must keep the OpSelectionMerge
|
|
// instruction on that branch.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%undef_int = OpUndef %uint
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
|
|
; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_merge]] 1 [[bb3:%\w+]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[sel_merge]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3
|
|
%bb3 = OpLabel
|
|
OpBranch %sel_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue3) {
|
|
// Checks that if a selection merge construct contains a conditional branch
|
|
// to the merge of a surrounding loop, the selection merge, and another block
|
|
// inside the selection merge, then we must keep the OpSelectionMerge
|
|
// instruction on that branch.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%undef_int = OpUndef %uint
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLabel
|
|
; CHECK: [[loop_header:%\w+]] = OpLabel
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_continue:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None
|
|
; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_continue]] 1 [[bb3:%\w+]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[sel_merge]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_continue]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_header]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpSwitch %undef_int %sel_merge 0 %cont 1 %bb3
|
|
%bb3 = OpLabel
|
|
OpBranch %sel_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop4) {
|
|
// Same as |SelectionMergeWithExitToLoop|, except the branch in the selection
|
|
// construct is an |OpSwitch| instead of an |OpConditionalBranch|. The
|
|
// OpSelectionMerge instruction is not needed in this case either.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%undef_int = OpUndef %uint
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_merge]] 1 [[bb3:%\w+]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpSwitch %undef_int %bb3 0 %loop_merge 1 %bb3
|
|
%bb3 = OpLabel
|
|
OpBranch %sel_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue4) {
|
|
// Same as |SelectionMergeWithExitToLoopContinue|, except the branch in the
|
|
// selection construct is an |OpSwitch| instead of an |OpConditionalBranch|.
|
|
// The OpSelectionMerge instruction is not needed in this case either.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%undef_int = OpUndef %uint
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_cont]] 1 [[bb3:%\w+]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[sel_merge:%\w+]]
|
|
; CHECK: [[sel_merge]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_merge]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %sel_merge None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpSwitch %undef_int %bb3 0 %cont 1 %bb3
|
|
%bb3 = OpLabel
|
|
OpBranch %sel_merge
|
|
%bb4 = OpLabel
|
|
OpBranch %sel_merge
|
|
%sel_merge = OpLabel
|
|
OpBranch %loop_merge
|
|
%cont = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeSameAsLoopContinue) {
|
|
// Same as |SelectionMergeWithExitToLoopContinue|, except the branch in the
|
|
// selection construct is an |OpSwitch| instead of an |OpConditionalBranch|.
|
|
// The OpSelectionMerge instruction is not needed in this case either.
|
|
const std::string predefs = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource GLSL 140
|
|
%void = OpTypeVoid
|
|
%func_type = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%uint = OpTypeInt 32 0
|
|
%undef_bool = OpUndef %bool
|
|
)";
|
|
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLabel
|
|
; CHECK: [[loop_header:%\w+]] = OpLabel
|
|
; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]]
|
|
; CHECK-NEXT: OpBranch [[bb1:%\w+]]
|
|
; CHECK: [[bb1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NEXT: OpSelectionMerge [[loop_cont]]
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb3:%\w+]] [[loop_cont]]
|
|
; CHECK: [[bb3]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[loop_cont]]
|
|
; CHECK: [[loop_cont]] = OpLabel
|
|
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[loop_header]] [[loop_merge]]
|
|
; CHECK: [[loop_merge]] = OpLabel
|
|
; CHECK-NEXT: OpReturn
|
|
%main = OpFunction %void None %func_type
|
|
%entry_bb = OpLabel
|
|
OpBranch %loop_header
|
|
%loop_header = OpLabel
|
|
OpLoopMerge %loop_merge %cont None
|
|
OpBranch %bb1
|
|
%bb1 = OpLabel
|
|
OpSelectionMerge %cont None
|
|
OpBranchConditional %true %bb2 %bb4
|
|
%bb2 = OpLabel
|
|
OpBranchConditional %undef_bool %bb3 %cont
|
|
%bb3 = OpLabel
|
|
OpBranch %cont
|
|
%bb4 = OpLabel
|
|
OpBranch %cont
|
|
%cont = OpLabel
|
|
OpBranchConditional %undef_bool %loop_header %loop_merge
|
|
%loop_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// The selection merge in the loop naming the continue target as merge is
|
|
// invalid, but handled by this pass so validation is disabled.
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, false);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) {
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpSelectionMerge [[merge1:%\w+]]
|
|
; CHECK: [[merge1]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[preheader:%\w+]]
|
|
; CHECK: [[preheader]] = OpLabel
|
|
; CHECK-NOT: OpLabel
|
|
; CHECK: OpBranch [[header:%\w+]]
|
|
; CHECK: [[header]] = OpLabel
|
|
; CHECK-NOT: OpLabel
|
|
; CHECK: OpLoopMerge [[merge2:%\w+]]
|
|
; CHECK: [[merge2]] = OpLabel
|
|
; CHECK-NEXT: OpUnreachable
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource ESSL 310
|
|
OpName %main "main"
|
|
OpName %h "h"
|
|
OpName %i "i"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%_ptr_Function_bool = OpTypePointer Function %bool
|
|
%true = OpConstantTrue %bool
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_1 = OpConstant %int 1
|
|
%int_0 = OpConstant %int 0
|
|
%27 = OpUndef %bool
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%h = OpVariable %_ptr_Function_bool Function
|
|
%i = OpVariable %_ptr_Function_int Function
|
|
OpSelectionMerge %11 None
|
|
OpBranchConditional %27 %10 %11
|
|
%10 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpSelectionMerge %14 None
|
|
OpBranchConditional %true %13 %14
|
|
%13 = OpLabel
|
|
OpStore %i %int_1
|
|
OpBranch %19
|
|
%19 = OpLabel
|
|
OpLoopMerge %21 %22 None
|
|
OpBranch %23
|
|
%23 = OpLabel
|
|
%26 = OpSGreaterThan %bool %int_1 %int_0
|
|
OpBranchConditional %true %20 %21
|
|
%20 = OpLabel
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
OpBranch %19
|
|
%21 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, DontFoldBackedge) {
|
|
const std::string body =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %2 "main"
|
|
OpExecutionMode %2 OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%2 = OpFunction %void None %4
|
|
%7 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
OpLoopMerge %9 %10 None
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%12 = OpUndef %bool
|
|
OpSelectionMerge %10 None
|
|
OpBranchConditional %12 %13 %10
|
|
%13 = OpLabel
|
|
OpBranch %9
|
|
%10 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpBranchConditional %false %8 %9
|
|
%9 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(body, body, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, FoldBackedgeToHeader) {
|
|
const std::string body =
|
|
R"(
|
|
; CHECK: OpLabel
|
|
; CHECK: [[header:%\w+]] = OpLabel
|
|
; CHECK-NEXT: OpLoopMerge {{%\w+}} [[cont:%\w+]]
|
|
; CHECK: [[cont]] = OpLabel
|
|
; This branch may not be in the continue block, but must come after it.
|
|
; CHECK: OpBranch [[header]]
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %2 "main"
|
|
OpExecutionMode %2 OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%2 = OpFunction %void None %4
|
|
%7 = OpLabel
|
|
OpBranch %8
|
|
%8 = OpLabel
|
|
OpLoopMerge %9 %10 None
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
%12 = OpUndef %bool
|
|
OpSelectionMerge %10 None
|
|
OpBranchConditional %12 %13 %10
|
|
%13 = OpLabel
|
|
OpBranch %9
|
|
%10 = OpLabel
|
|
OpBranch %14
|
|
%14 = OpLabel
|
|
OpBranchConditional %true %8 %9
|
|
%9 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// The selection merge in the loop naming the continue target as merge is
|
|
// invalid, but handled by this pass so validation is disabled.
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(body, false);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) {
|
|
const std::string spirv = R"(
|
|
; CHECK: OpLabel
|
|
; CHECK: [[outer:%\w+]] = OpLabel
|
|
; CHECK-NEXT: OpLoopMerge [[outer_merge:%\w+]] [[outer_cont:%\w+]] None
|
|
; CHECK-NEXT: OpBranch [[inner:%\w+]]
|
|
; CHECK: [[inner]] = OpLabel
|
|
; CHECK: OpLoopMerge [[outer_cont]] [[inner_cont:%\w+]] None
|
|
; CHECK: [[inner_cont]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[inner]]
|
|
; CHECK: [[outer_cont]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[outer]]
|
|
; CHECK: [[outer_merge]] = OpLabel
|
|
; CHECK-NEXT: OpUnreachable
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint GLCompute %main "main"
|
|
OpExecutionMode %main LocalSize 1 1 1
|
|
%void = OpTypeVoid
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%void_fn = OpTypeFunction %void
|
|
%main = OpFunction %void None %void_fn
|
|
%entry = OpLabel
|
|
OpBranch %outer_loop
|
|
%outer_loop = OpLabel
|
|
OpLoopMerge %outer_merge %outer_continue None
|
|
OpBranch %inner_loop
|
|
%inner_loop = OpLabel
|
|
OpLoopMerge %outer_continue %inner_continue None
|
|
OpBranch %inner_body
|
|
%inner_body = OpLabel
|
|
OpSelectionMerge %inner_continue None
|
|
OpBranchConditional %true %ret %inner_continue
|
|
%ret = OpLabel
|
|
OpReturn
|
|
%inner_continue = OpLabel
|
|
OpBranchConditional %true %outer_continue %inner_loop
|
|
%outer_continue = OpLabel
|
|
OpBranchConditional %true %outer_merge %outer_loop
|
|
%outer_merge = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(spirv, true);
|
|
}
|
|
|
|
// Fold a switch with a nested break. The only case should be the default.
|
|
TEST_F(DeadBranchElimTest, FoldSwitchWithNestedBreak) {
|
|
const std::string spirv = R"(
|
|
; CHECK: OpSwitch %int_3 [[case_bb:%\w+]]{{[[:space:]]}}
|
|
; CHECK: [[case_bb]] = OpLabel
|
|
; CHECK-NEXT: OpUndef
|
|
; CHECK-NEXT: OpSelectionMerge
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %2 "main"
|
|
OpSource GLSL 450
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_3 = OpConstant %int 3
|
|
%int_1 = OpConstant %int 1
|
|
%bool = OpTypeBool
|
|
%2 = OpFunction %void None %4
|
|
%10 = OpLabel
|
|
OpSelectionMerge %11 None
|
|
OpSwitch %int_3 %12 3 %13
|
|
%12 = OpLabel
|
|
OpBranch %11
|
|
%13 = OpLabel
|
|
%14 = OpUndef %bool
|
|
OpSelectionMerge %15 None
|
|
OpBranchConditional %14 %16 %15
|
|
%16 = OpLabel
|
|
OpBranch %11
|
|
%15 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(spirv, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, FoldBranchWithBreakToSwitch) {
|
|
const std::string spirv = R"(
|
|
; CHECK: OpSelectionMerge [[sel_merge:%\w+]]
|
|
; CHECK-NEXT: OpSwitch {{%\w+}} {{%\w+}} 3 [[bb:%\w+]]
|
|
; CHECK: [[bb]] = OpLabel
|
|
; CHECK-NEXT: OpBranch [[bb2:%\w+]]
|
|
; CHECK: [[bb2]] = OpLabel
|
|
; CHECK-NOT: OpSelectionMerge
|
|
; CHECK: OpFunctionEnd
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %2 "main"
|
|
OpSource GLSL 450
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%_ptr_Function_int = OpTypePointer Function %int
|
|
%int_3 = OpConstant %int 3
|
|
%int_1 = OpConstant %int 1
|
|
%bool = OpTypeBool
|
|
%true = OpConstantTrue %bool
|
|
%2 = OpFunction %void None %4
|
|
%10 = OpLabel
|
|
%undef_int = OpUndef %int
|
|
OpSelectionMerge %11 None
|
|
OpSwitch %undef_int %12 3 %13
|
|
%12 = OpLabel
|
|
OpBranch %11
|
|
%13 = OpLabel
|
|
OpSelectionMerge %15 None
|
|
OpBranchConditional %true %16 %15
|
|
%16 = OpLabel
|
|
%14 = OpUndef %bool
|
|
OpBranchConditional %14 %11 %17
|
|
%17 = OpLabel
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndMatch<DeadBranchElimPass>(spirv, true);
|
|
}
|
|
|
|
TEST_F(DeadBranchElimTest, IfInSwitch) {
|
|
// #version 310 es
|
|
//
|
|
// void main()
|
|
// {
|
|
// switch(0)
|
|
// {
|
|
// case 0:
|
|
// if(false)
|
|
// {
|
|
// }
|
|
// else
|
|
// {
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
const std::string before =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource ESSL 310
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%int_0 = OpConstant %int 0
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpSelectionMerge %9 None
|
|
OpSwitch %int_0 %9 0 %8
|
|
%8 = OpLabel
|
|
OpSelectionMerge %13 None
|
|
OpBranchConditional %false %12 %13
|
|
%12 = OpLabel
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
OpBranch %9
|
|
%9 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource ESSL 310
|
|
OpName %main "main"
|
|
%void = OpTypeVoid
|
|
%4 = OpTypeFunction %void
|
|
%int = OpTypeInt 32 1
|
|
%int_0 = OpConstant %int 0
|
|
%bool = OpTypeBool
|
|
%false = OpConstantFalse %bool
|
|
%main = OpFunction %void None %4
|
|
%9 = OpLabel
|
|
OpBranch %11
|
|
%11 = OpLabel
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpBranch %10
|
|
%10 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SinglePassRunAndCheck<DeadBranchElimPass>(before, after, true, true);
|
|
}
|
|
|
|
// TODO(greg-lunarg): Add tests to verify handling of these cases:
|
|
//
|
|
// More complex control flow
|
|
// Others?
|
|
|
|
} // namespace
|
|
} // namespace opt
|
|
} // namespace spvtools
|