SPIRV-Tools/test/opt/dead_branch_elim_test.cpp
Steven Perron 50f307f889 Simplify OpPhi instructions referencing unreachable continues
In dead branch elimination, we already recognize unreachable continue
blocks, and update OpPhi instruction accordingly.  This change adds an
extra check: if the head block has exactly 1 other incoming edge, then
replace the OpPhi with the value from that edge.

Fixes #1314.
2018-02-16 18:58:03 -05:00

2013 lines
52 KiB
C++

// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "pass_fixture.h"
#include "pass_utils.h"
namespace {
using namespace spvtools;
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<opt::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<opt::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<opt::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<opt::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<opt::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<opt::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
%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
)";
SinglePassRunAndCheck<opt::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<opt::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<opt::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<opt::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<opt::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<opt::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.1,0.1,0.1,0.1);
// break;
// case 2:
// OutColor = vec4(0.2,0.2,0.2,0.2);
// 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_1 = OpConstant %float 0.1
%15 = OpConstantComposite %v4float %float_0_1 %float_0_1 %float_0_1 %float_0_1
%float_0_2 = OpConstant %float 0.2
%17 = OpConstantComposite %v4float %float_0_2 %float_0_2 %float_0_2 %float_0_2
%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<opt::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.1,0.1,0.1,0.1);
// break;
// case 2:
// OutColor = vec4(0.2,0.2,0.2,0.2);
// 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_1 = OpConstant %float 0.1
%15 = OpConstantComposite %v4float %float_0_1 %float_0_1 %float_0_1 %float_0_1
%float_0_2 = OpConstant %float 0.2
%17 = OpConstantComposite %v4float %float_0_2 %float_0_2 %float_0_2 %float_0_2
%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<opt::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_1 = OpConstant %float 0.1
%19 = OpConstantComposite %v4float %float_0_1 %float_0_1 %float_0_1 %float_0_1
%float_0_2 = OpConstant %float 0.2
%21 = OpConstantComposite %v4float %float_0_2 %float_0_2 %float_0_2 %float_0_2
%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<opt::DeadBranchElimPass>(predefs + before,
predefs + after, true, true);
}
#ifdef SPIRV_EFFCEE
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<opt::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<opt::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<opt::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<opt::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<opt::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<opt::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<opt::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<opt::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: [[br]] = OpLabel
; CHECK-NEXT: OpBranch [[merge]]
; CHECK-NEXT: [[merge]] = OpLabel
; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool %true %2 %false [[br]]
; CHECK-NEXT: OpLogicalNot %bool [[phi]]
; 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
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<opt::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<opt::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<opt::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: [[continue]] = OpLabel
; CHECK-NEXT: OpBranch [[header]]
; CHECK-NEXT: [[merge]] = OpLabel
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<opt::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<opt::DeadBranchElimPass>(text, true);
EXPECT_EQ(std::get<1>(result), opt::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: [[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]]
; CHECK-NEXT: [[merge]] = OpLabel
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<opt::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<opt::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<opt::DeadBranchElimPass>(text, true);
}
#endif
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// More complex control flow
// Others?
} // anonymous namespace