| // 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, IfThenElseNull) { |
| // For booleans OpConstantNull should be treated similar to OpConstantFalse. |
| // |
| // From the SPIR-V spec: |
| // OpConstantNull: Declares a new null constant value. |
| // The null value is type dependent, defined as follows: |
| // - Scalar Boolean: false |
| // ... |
| |
| 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 |
| %9 = OpConstantNull %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 %9 %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); |
| } |
| |
| TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithSingleCase) { |
| const std::string text = R"(OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %bool = OpTypeBool |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %8 = OpUndef %bool |
| %main = OpFunction %void None %4 |
| %9 = OpLabel |
| OpSelectionMerge %10 None |
| OpSwitch %uint_0 %11 |
| %11 = OpLabel |
| OpSelectionMerge %12 None |
| OpBranchConditional %8 %10 %12 |
| %12 = OpLabel |
| OpBranch %10 |
| %10 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndCheck<DeadBranchElimPass>(text, text, true, true); |
| } |
| |
| TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithTwoCases) { |
| const std::string text = R"( |
| ; CHECK: OpSelectionMerge [[merge:%\w+]] None |
| ; CHECK-NEXT: OpSwitch %uint_0 [[bb:%\w+\n]] |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 450 |
| OpName %main "main" |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %bool = OpTypeBool |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %8 = OpUndef %bool |
| %main = OpFunction %void None %4 |
| %9 = OpLabel |
| OpSelectionMerge %10 None |
| OpSwitch %uint_0 %11 1 %12 |
| %11 = OpLabel |
| OpSelectionMerge %13 None |
| OpBranchConditional %8 %10 %13 |
| %13 = OpLabel |
| OpBranch %10 |
| %12 = OpLabel |
| OpBranch %10 |
| %10 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<DeadBranchElimPass>(text, true); |
| } |
| |
| TEST_F(DeadBranchElimTest, DebugInformation) { |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| %ext = OpExtInstImport "OpenCL.DebugInfo.100" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %gl_FragColor |
| OpExecutionMode %main OriginUpperLeft |
| OpSource GLSL 140 |
| %name = OpString "test" |
| 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 |
| |
| ; CHECK: [[value:%\w+]] = OpConstantComposite %v4float %float_0 %float_0 %float_0 %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 |
| %uint = OpTypeInt 32 0 |
| %uint_32 = OpConstant %uint 32 |
| |
| %null_expr = OpExtInst %void %ext DebugExpression |
| %src = OpExtInst %void %ext DebugSource %name |
| %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL |
| %ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void |
| %dbg_main = OpExtInst %void %ext DebugFunction %name %ty %src 0 0 %cu %name FlagIsProtected|FlagIsPrivate 0 %main |
| |
| ; CHECK: [[bb1:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLexicalBlock [[src:%\w+]] 1 0 [[dbg_main:%\w+]] |
| ; CHECK: [[bb2:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 2 0 [[dbg_main]] |
| ; CHECK: [[bb3:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 3 0 [[dbg_main]] |
| %bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main |
| %bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main |
| %bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main |
| |
| %dbg_f = OpExtInst %void %ext DebugTypeBasic %name %uint_32 Float |
| ; CHECK: [[dbg_foo:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty:%\w+]] [[src]] 0 0 [[dbg_main]] |
| %dbg_foo = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 0 0 %dbg_main FlagIsLocal |
| ; CHECK: [[dbg_bar:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty]] [[src]] 1 0 [[bb3]] |
| %dbg_bar = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 1 0 %bb3 FlagIsLocal |
| |
| %main = OpFunction %void None %5 |
| %17 = OpLabel |
| ; CHECK-NOT: DebugScope [[dbg_main]] |
| ; CHECK-NOT: OpLine {{%\w+}} 0 0 |
| %scope0 = OpExtInst %void %ext DebugScope %dbg_main |
| OpLine %name 0 0 |
| OpSelectionMerge %18 None |
| OpBranchConditional %true %19 %20 |
| %19 = OpLabel |
| ; CHECK: DebugScope [[bb1]] |
| ; CHECK: OpLine {{%\w+}} 1 0 |
| %scope1 = OpExtInst %void %ext DebugScope %bb1 |
| OpLine %name 1 0 |
| OpBranch %18 |
| %20 = OpLabel |
| ; CHECK-NOT: DebugScope [[bb2]] |
| ; CHECK-NOT: OpLine {{%\w+}} 2 0 |
| %scope2 = OpExtInst %void %ext DebugScope %bb2 |
| OpLine %name 2 0 |
| OpBranch %18 |
| %18 = OpLabel |
| |
| ; CHECK: DebugScope [[bb3]] |
| ; CHECK: OpLine {{%\w+}} 3 0 |
| ; CHECK: DebugValue [[dbg_foo]] [[value]] |
| ; CHECK: OpLine {{%\w+}} 4 0 |
| ; CHECK: OpStore %gl_FragColor [[value]] |
| ; CHECK: DebugDeclare [[dbg_bar]] %gl_FragColor |
| ; CHECK: DebugValue [[dbg_bar]] [[value]] |
| %scope3 = OpExtInst %void %ext DebugScope %bb3 |
| OpLine %name 3 0 |
| %21 = OpPhi %v4float %12 %19 %14 %20 |
| %decl0 = OpExtInst %void %ext DebugValue %dbg_foo %21 %null_expr |
| OpLine %name 4 0 |
| OpStore %gl_FragColor %21 |
| %decl1 = OpExtInst %void %ext DebugDeclare %dbg_bar %gl_FragColor %null_expr |
| %decl2 = OpExtInst %void %ext DebugValue %dbg_bar %21 %null_expr |
| OpLine %name 5 0 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<DeadBranchElimPass>(text, true); |
| } |
| |
| // TODO(greg-lunarg): Add tests to verify handling of these cases: |
| // |
| // More complex control flow |
| // Others? |
| |
| } // namespace |
| } // namespace opt |
| } // namespace spvtools |