Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/KhronosGroup/SPIRV-Tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGreg Fischer <greg@lunarg.com>2022-11-02 20:23:25 +0300
committerGitHub <noreply@github.com>2022-11-02 20:23:25 +0300
commitc8e1588cfa3ff9e3b5d600ef04f4261c4e68af90 (patch)
tree05fd13d996634c2c11280b092a88b812dab3fdb9 /test
parenta52de681dd17f8b545ecd9ea2138f72b39bf449a (diff)
Add passes to eliminate dead output stores (#4970)
This adds two passes to accomplish this: one pass to analyze a shader to determine the input slots that are live. The second pass is run on the preceding shader to eliminate any stores to output slots that are not consumed by the following shader. These passes support vert, tesc, tese, geom, and frag shaders. These passes are currently only available through the API. These passes together with dead code elimination, and elimination of dead input and output components and variables (WIP), will allow users to do dead code elimination across shader boundaries.
Diffstat (limited to 'test')
-rw-r--r--test/opt/CMakeLists.txt2
-rw-r--r--test/opt/analyze_live_input_test.cpp910
-rw-r--r--test/opt/eliminate_dead_output_stores_test.cpp952
3 files changed, 1864 insertions, 0 deletions
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 15966c184..36bf04c32 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -18,6 +18,7 @@ add_subdirectory(loop_optimizations)
add_spvtools_unittest(TARGET opt
SRCS aggressive_dead_code_elim_test.cpp
amd_ext_to_khr.cpp
+ analyze_live_input_test.cpp
assembly_builder_test.cpp
block_merge_test.cpp
ccp_test.cpp
@@ -44,6 +45,7 @@ add_spvtools_unittest(TARGET opt
eliminate_dead_functions_test.cpp
eliminate_dead_input_components_test.cpp
eliminate_dead_member_test.cpp
+ eliminate_dead_output_stores_test.cpp
feature_manager_test.cpp
fix_func_call_arguments_test.cpp
fix_storage_class_test.cpp
diff --git a/test/opt/analyze_live_input_test.cpp b/test/opt/analyze_live_input_test.cpp
new file mode 100644
index 000000000..d61dbdb85
--- /dev/null
+++ b/test/opt/analyze_live_input_test.cpp
@@ -0,0 +1,910 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 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 <unordered_set>
+
+#include "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using AnalyzeLiveInputTest = PassTest<::testing::Test>;
+
+TEST_F(AnalyzeLiveInputTest, FragMultipleLocations) {
+ // Should report locations {2, 5}
+ //
+ // #version 450
+ //
+ // layout(location = 2) in Vertex
+ // {
+ // vec4 color0;
+ // vec4 color1;
+ // vec4 color2[3];
+ // } iVert;
+ //
+ // layout(location = 0) out vec4 oFragColor;
+ //
+ // void main()
+ // {
+ // oFragColor = iVert.color0 + iVert.color2[1];
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %oFragColor %iVert
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %oFragColor "oFragColor"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "color0"
+ OpMemberName %Vertex 1 "color1"
+ OpMemberName %Vertex 2 "color2"
+ OpName %iVert "iVert"
+ OpDecorate %oFragColor Location 0
+ OpDecorate %Vertex Block
+ OpDecorate %iVert Location 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %oFragColor = OpVariable %_ptr_Output_v4float Output
+ %uint = OpTypeInt 32 0
+ %uint_3 = OpConstant %uint 3
+%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
+ %Vertex = OpTypeStruct %v4float %v4float %_arr_v4float_uint_3
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+ %iVert = OpVariable %_ptr_Input_Vertex Input
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %int_2 = OpConstant %int 2
+ %int_1 = OpConstant %int 1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %19 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+ %20 = OpLoad %v4float %19
+ %23 = OpAccessChain %_ptr_Input_v4float %iVert %int_2 %int_1
+ %24 = OpLoad %v4float %23
+ %25 = OpFAdd %v4float %20 %24
+ OpStore %oFragColor %25
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_inputs.find(0);
+ auto itr1 = live_inputs.find(1);
+ auto itr2 = live_inputs.find(2);
+ auto itr3 = live_inputs.find(3);
+ auto itr4 = live_inputs.find(4);
+ auto itr5 = live_inputs.find(5);
+ auto itr6 = live_inputs.find(6);
+
+ // Expect live_inputs == {2, 5}
+ EXPECT_TRUE(itr0 == live_inputs.end());
+ EXPECT_TRUE(itr1 == live_inputs.end());
+ EXPECT_TRUE(itr2 != live_inputs.end());
+ EXPECT_TRUE(itr3 == live_inputs.end());
+ EXPECT_TRUE(itr4 == live_inputs.end());
+ EXPECT_TRUE(itr5 != live_inputs.end());
+ EXPECT_TRUE(itr6 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, FragMatrix) {
+ // Should report locations {2, 8, 9, 10, 11}
+ //
+ // #version 450
+ //
+ // uniform ui_name {
+ // int i;
+ // } ui_inst;
+ //
+ // layout(location = 2) in Vertex
+ // {
+ // vec4 color0;
+ // vec4 color1;
+ // mat4 color2;
+ // mat4 color3;
+ // mat4 color4;
+ // } iVert;
+ //
+ // // Output variable for the color
+ // layout(location = 0) out vec4 oFragColor;
+ //
+ // void main()
+ // {
+ // oFragColor = iVert.color0 + iVert.color3[ui_inst.i];
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %oFragColor %iVert %ui_inst
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %oFragColor "oFragColor"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "color0"
+ OpMemberName %Vertex 1 "color1"
+ OpMemberName %Vertex 2 "color2"
+ OpMemberName %Vertex 3 "color3"
+ OpMemberName %Vertex 4 "color4"
+ OpName %iVert "iVert"
+ OpName %ui_name "ui_name"
+ OpMemberName %ui_name 0 "i"
+ OpName %ui_inst "ui_inst"
+ OpDecorate %oFragColor Location 0
+ OpDecorate %Vertex Block
+ OpDecorate %iVert Location 2
+ OpMemberDecorate %ui_name 0 Offset 0
+ OpDecorate %ui_name Block
+ OpDecorate %ui_inst DescriptorSet 0
+ OpDecorate %ui_inst Binding 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %oFragColor = OpVariable %_ptr_Output_v4float Output
+%mat4v4float = OpTypeMatrix %v4float 4
+ %Vertex = OpTypeStruct %v4float %v4float %mat4v4float %mat4v4float %mat4v4float
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+ %iVert = OpVariable %_ptr_Input_Vertex Input
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %int_3 = OpConstant %int 3
+ %ui_name = OpTypeStruct %int
+%_ptr_Uniform_ui_name = OpTypePointer Uniform %ui_name
+ %ui_inst = OpVariable %_ptr_Uniform_ui_name Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+ %18 = OpLoad %v4float %17
+ %24 = OpAccessChain %_ptr_Uniform_int %ui_inst %int_0
+ %25 = OpLoad %int %24
+ %26 = OpAccessChain %_ptr_Input_v4float %iVert %int_3 %25
+ %27 = OpLoad %v4float %26
+ %28 = OpFAdd %v4float %18 %27
+ OpStore %oFragColor %28
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_inputs.find(0);
+ auto itr1 = live_inputs.find(1);
+ auto itr2 = live_inputs.find(2);
+ auto itr3 = live_inputs.find(3);
+ auto itr4 = live_inputs.find(4);
+ auto itr5 = live_inputs.find(5);
+ auto itr6 = live_inputs.find(6);
+ auto itr7 = live_inputs.find(7);
+ auto itr8 = live_inputs.find(8);
+ auto itr9 = live_inputs.find(9);
+ auto itr10 = live_inputs.find(10);
+ auto itr11 = live_inputs.find(11);
+ auto itr12 = live_inputs.find(12);
+ auto itr13 = live_inputs.find(13);
+ auto itr14 = live_inputs.find(14);
+ auto itr15 = live_inputs.find(15);
+
+ // Expect live_inputs == {2, 8, 9, 10, 11}
+ EXPECT_TRUE(itr0 == live_inputs.end());
+ EXPECT_TRUE(itr1 == live_inputs.end());
+ EXPECT_TRUE(itr2 != live_inputs.end());
+ EXPECT_TRUE(itr3 == live_inputs.end());
+ EXPECT_TRUE(itr4 == live_inputs.end());
+ EXPECT_TRUE(itr5 == live_inputs.end());
+ EXPECT_TRUE(itr6 == live_inputs.end());
+ EXPECT_TRUE(itr7 == live_inputs.end());
+ EXPECT_TRUE(itr8 != live_inputs.end());
+ EXPECT_TRUE(itr9 != live_inputs.end());
+ EXPECT_TRUE(itr10 != live_inputs.end());
+ EXPECT_TRUE(itr11 != live_inputs.end());
+ EXPECT_TRUE(itr12 == live_inputs.end());
+ EXPECT_TRUE(itr13 == live_inputs.end());
+ EXPECT_TRUE(itr14 == live_inputs.end());
+ EXPECT_TRUE(itr15 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, FragMemberLocs) {
+ // Should report location {1}
+ //
+ // #version 450
+ //
+ // in Vertex
+ // {
+ // layout (location = 1) vec4 Cd;
+ // layout (location = 0) vec2 uv;
+ // } iVert;
+ //
+ // layout (location = 0) out vec4 fragColor;
+ //
+ // void main()
+ // {
+ // vec4 color = vec4(iVert.Cd);
+ // fragColor = color;
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %iVert %fragColor
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %color "color"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "Cd"
+ OpMemberName %Vertex 1 "uv"
+ OpName %iVert "iVert"
+ OpName %fragColor "fragColor"
+ OpMemberDecorate %Vertex 0 Location 1
+ OpMemberDecorate %Vertex 1 Location 0
+ OpDecorate %Vertex Block
+ OpDecorate %fragColor Location 0
+ OpDecorate %_struct_27 Block
+ OpMemberDecorate %_struct_27 0 Location 1
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %v2float = OpTypeVector %float 2
+ %Vertex = OpTypeStruct %v4float %v2float
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %fragColor = OpVariable %_ptr_Output_v4float Output
+ %_struct_27 = OpTypeStruct %v4float
+%_ptr_Input__struct_27 = OpTypePointer Input %_struct_27
+ %iVert = OpVariable %_ptr_Input__struct_27 Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %color = OpVariable %_ptr_Function_v4float Function
+ %17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
+ %18 = OpLoad %v4float %17
+ %19 = OpCompositeExtract %float %18 0
+ %20 = OpCompositeExtract %float %18 1
+ %21 = OpCompositeExtract %float %18 2
+ %22 = OpCompositeExtract %float %18 3
+ %23 = OpCompositeConstruct %v4float %19 %20 %21 %22
+ OpStore %color %23
+ %26 = OpLoad %v4float %color
+ OpStore %fragColor %26
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_inputs.find(0);
+ auto itr1 = live_inputs.find(1);
+
+ // Expect live_inputs == {2, 5}
+ EXPECT_TRUE(itr0 == live_inputs.end());
+ EXPECT_TRUE(itr1 != live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, ArrayedInput) {
+ // Tests handling of arrayed input seen in Tesc, Tese and Geom shaders.
+ //
+ // Should report location {1, 10}.
+ //
+ // #version 450
+ //
+ // layout (vertices = 4) out;
+ //
+ // layout (location = 1) in Vertex
+ // {
+ // vec4 p;
+ // vec3 n;
+ // vec4 f[100];
+ // } iVert[];
+ //
+ // layout (location = 0) out vec4 position[4];
+ //
+ // void main()
+ // {
+ // vec4 pos = iVert[gl_InvocationID].p *
+ // iVert[gl_InvocationID].f[7];
+ // position[gl_InvocationID] = pos;
+ // }
+ const std::string text = R"(
+ OpCapability Tessellation
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationControl %main "main" %iVert %gl_InvocationID %position
+ OpExecutionMode %main OutputVertices 4
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "p"
+ OpMemberName %Vertex 1 "n"
+ OpMemberName %Vertex 2 "f"
+ OpName %iVert "iVert"
+ OpName %gl_InvocationID "gl_InvocationID"
+ OpName %position "position"
+ OpDecorate %Vertex Block
+ OpDecorate %iVert Location 1
+ OpDecorate %gl_InvocationID BuiltIn InvocationId
+ OpDecorate %position Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %v3float = OpTypeVector %float 3
+ %uint = OpTypeInt 32 0
+ %uint_100 = OpConstant %uint 100
+%_arr_v4float_uint_100 = OpTypeArray %v4float %uint_100
+ %Vertex = OpTypeStruct %v4float %v3float %_arr_v4float_uint_100
+ %uint_32 = OpConstant %uint 32
+%_arr_Vertex_uint_32 = OpTypeArray %Vertex %uint_32
+%_ptr_Input__arr_Vertex_uint_32 = OpTypePointer Input %_arr_Vertex_uint_32
+ %iVert = OpVariable %_ptr_Input__arr_Vertex_uint_32 Input
+ %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %int_2 = OpConstant %int 2
+ %int_7 = OpConstant %int 7
+ %uint_4 = OpConstant %uint 4
+%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4
+%_ptr_Output__arr_v4float_uint_4 = OpTypePointer Output %_arr_v4float_uint_4
+ %position = OpVariable %_ptr_Output__arr_v4float_uint_4 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %22 = OpLoad %int %gl_InvocationID
+ %25 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_0
+ %26 = OpLoad %v4float %25
+ %30 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_2 %int_7
+ %31 = OpLoad %v4float %30
+ %32 = OpFMul %v4float %26 %31
+ %40 = OpAccessChain %_ptr_Output_v4float %position %22
+ OpStore %40 %32
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_inputs.find(0);
+ auto itr1 = live_inputs.find(1);
+ auto itr2 = live_inputs.find(2);
+ auto itr3 = live_inputs.find(3);
+ auto itr4 = live_inputs.find(4);
+ auto itr5 = live_inputs.find(5);
+ auto itr6 = live_inputs.find(6);
+ auto itr7 = live_inputs.find(7);
+ auto itr8 = live_inputs.find(8);
+ auto itr9 = live_inputs.find(9);
+ auto itr10 = live_inputs.find(10);
+ auto itr11 = live_inputs.find(11);
+
+ // Expect live_inputs == {1, 10}
+ EXPECT_TRUE(itr0 == live_inputs.end());
+ EXPECT_TRUE(itr1 != live_inputs.end());
+ EXPECT_TRUE(itr2 == live_inputs.end());
+ EXPECT_TRUE(itr3 == live_inputs.end());
+ EXPECT_TRUE(itr4 == live_inputs.end());
+ EXPECT_TRUE(itr5 == live_inputs.end());
+ EXPECT_TRUE(itr6 == live_inputs.end());
+ EXPECT_TRUE(itr7 == live_inputs.end());
+ EXPECT_TRUE(itr8 == live_inputs.end());
+ EXPECT_TRUE(itr9 == live_inputs.end());
+ EXPECT_TRUE(itr10 != live_inputs.end());
+ EXPECT_TRUE(itr11 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, ArrayedInputMemberLocs) {
+ // Tests handling of member locs with arrayed input seen in Tesc, Tese
+ // and Geom shaders.
+ //
+ // Should report location {1, 12}.
+ //
+ // #version 450
+ //
+ // layout (vertices = 4) out;
+ //
+ // in Vertex
+ // {
+ // layout (location = 1) vec4 p;
+ // layout (location = 3) vec3 n;
+ // layout (location = 5) vec4 f[100];
+ // } iVert[];
+ //
+ // layout (location = 0) out vec4 position[4];
+ //
+ // void main()
+ // {
+ // vec4 pos = iVert[gl_InvocationID].p *
+ // iVert[gl_InvocationID].f[7];
+ // position[gl_InvocationID] = pos;
+ // }
+ const std::string text = R"(
+ OpCapability Tessellation
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationControl %main "main" %iVert %gl_InvocationID %position
+ OpExecutionMode %main OutputVertices 4
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "p"
+ OpMemberName %Vertex 1 "n"
+ OpMemberName %Vertex 2 "f"
+ OpName %iVert "iVert"
+ OpName %gl_InvocationID "gl_InvocationID"
+ OpName %position "position"
+ OpMemberDecorate %Vertex 0 Location 1
+ OpMemberDecorate %Vertex 1 Location 3
+ OpMemberDecorate %Vertex 2 Location 5
+ OpDecorate %Vertex Block
+ OpDecorate %gl_InvocationID BuiltIn InvocationId
+ OpDecorate %position Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %v3float = OpTypeVector %float 3
+ %uint = OpTypeInt 32 0
+ %uint_100 = OpConstant %uint 100
+%_arr_v4float_uint_100 = OpTypeArray %v4float %uint_100
+ %Vertex = OpTypeStruct %v4float %v3float %_arr_v4float_uint_100
+ %uint_32 = OpConstant %uint 32
+%_arr_Vertex_uint_32 = OpTypeArray %Vertex %uint_32
+%_ptr_Input__arr_Vertex_uint_32 = OpTypePointer Input %_arr_Vertex_uint_32
+ %iVert = OpVariable %_ptr_Input__arr_Vertex_uint_32 Input
+ %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %int_2 = OpConstant %int 2
+ %int_7 = OpConstant %int 7
+ %uint_4 = OpConstant %uint 4
+%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4
+%_ptr_Output__arr_v4float_uint_4 = OpTypePointer Output %_arr_v4float_uint_4
+ %position = OpVariable %_ptr_Output__arr_v4float_uint_4 Output
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %22 = OpLoad %int %gl_InvocationID
+ %25 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_0
+ %26 = OpLoad %v4float %25
+ %30 = OpAccessChain %_ptr_Input_v4float %iVert %22 %int_2 %int_7
+ %31 = OpLoad %v4float %30
+ %32 = OpFMul %v4float %26 %31
+ %40 = OpAccessChain %_ptr_Output_v4float %position %22
+ OpStore %40 %32
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_inputs.find(0);
+ auto itr1 = live_inputs.find(1);
+ auto itr2 = live_inputs.find(2);
+ auto itr3 = live_inputs.find(3);
+ auto itr4 = live_inputs.find(4);
+ auto itr5 = live_inputs.find(5);
+ auto itr6 = live_inputs.find(6);
+ auto itr7 = live_inputs.find(7);
+ auto itr8 = live_inputs.find(8);
+ auto itr9 = live_inputs.find(9);
+ auto itr10 = live_inputs.find(10);
+ auto itr11 = live_inputs.find(11);
+ auto itr12 = live_inputs.find(12);
+ auto itr13 = live_inputs.find(13);
+
+ // Expect live_inputs == {1, 12}
+ EXPECT_TRUE(itr0 == live_inputs.end());
+ EXPECT_TRUE(itr1 != live_inputs.end());
+ EXPECT_TRUE(itr2 == live_inputs.end());
+ EXPECT_TRUE(itr3 == live_inputs.end());
+ EXPECT_TRUE(itr4 == live_inputs.end());
+ EXPECT_TRUE(itr5 == live_inputs.end());
+ EXPECT_TRUE(itr6 == live_inputs.end());
+ EXPECT_TRUE(itr7 == live_inputs.end());
+ EXPECT_TRUE(itr8 == live_inputs.end());
+ EXPECT_TRUE(itr9 == live_inputs.end());
+ EXPECT_TRUE(itr10 == live_inputs.end());
+ EXPECT_TRUE(itr11 == live_inputs.end());
+ EXPECT_TRUE(itr12 != live_inputs.end());
+ EXPECT_TRUE(itr13 == live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, Builtins) {
+ // Tests handling of builtin input seen in Tesc, Tese and Geom shaders.
+ //
+ // Should report builtin gl_PointSize only.
+ //
+ // #version 460
+ //
+ // layout(triangle_strip, max_vertices = 3) out;
+ // layout(triangles) in;
+ //
+ // void main()
+ // {
+ // for (int i = 0; i < 3; i++)
+ // {
+ // gl_Position = gl_in[i].gl_Position;
+ // gl_PointSize = gl_in[i].gl_PointSize;
+ //
+ // EmitVertex();
+ // }
+ //
+ // EndPrimitive();
+ // }
+ const std::string text = R"(
+ OpCapability Geometry
+ OpCapability GeometryPointSize
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Geometry %main "main" %_ %gl_in
+ OpExecutionMode %main Triangles
+ OpExecutionMode %main Invocations 1
+ OpExecutionMode %main OutputTriangleStrip
+ OpExecutionMode %main OutputVertices 3
+ OpSource GLSL 460
+ OpName %main "main"
+ OpName %i "i"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpMemberName %gl_PerVertex 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+ OpName %_ ""
+ OpName %gl_PerVertex_0 "gl_PerVertex"
+ OpMemberName %gl_PerVertex_0 0 "gl_Position"
+ OpMemberName %gl_PerVertex_0 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex_0 3 "gl_CullDistance"
+ OpName %gl_in "gl_in"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+ OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+ OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+ OpDecorate %gl_PerVertex Block
+ OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize
+ OpDecorate %gl_PerVertex_0 Block
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %int_0 = OpConstant %int 0
+ %int_3 = OpConstant %int 3
+ %bool = OpTypeBool
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+%gl_PerVertex_0 = OpTypeStruct %v4float %float
+ %uint_3 = OpConstant %uint 3
+%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3
+%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3
+ %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %int_1 = OpConstant %int 1
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_float = OpTypePointer Output %float
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %i = OpVariable %_ptr_Function_int Function
+ OpStore %i %int_0
+ OpBranch %10
+ %10 = OpLabel
+ OpLoopMerge %12 %13 None
+ OpBranch %14
+ %14 = OpLabel
+ %15 = OpLoad %int %i
+ %18 = OpSLessThan %bool %15 %int_3
+ OpBranchConditional %18 %11 %12
+ %11 = OpLabel
+ %32 = OpLoad %int %i
+ %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0
+ %35 = OpLoad %v4float %34
+ %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %37 %35
+ %39 = OpLoad %int %i
+ %41 = OpAccessChain %_ptr_Input_float %gl_in %39 %int_1
+ %42 = OpLoad %float %41
+ %44 = OpAccessChain %_ptr_Output_float %_ %int_1
+ OpStore %44 %42
+ OpEmitVertex
+ OpBranch %13
+ %13 = OpLabel
+ %45 = OpLoad %int %i
+ %46 = OpIAdd %int %45 %int_1
+ OpStore %i %46
+ OpBranch %10
+ %12 = OpLabel
+ OpEndPrimitive
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_builtins.find(SpvBuiltInPointSize);
+ auto itr1 = live_builtins.find(SpvBuiltInClipDistance);
+ auto itr2 = live_builtins.find(SpvBuiltInCullDistance);
+
+ // Expect live_builtins == { SpvBuiltInPointSize }
+ EXPECT_TRUE(itr0 != live_builtins.end());
+ EXPECT_TRUE(itr1 == live_builtins.end());
+ EXPECT_TRUE(itr2 == live_builtins.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, ArrayedInputPatchLocs) {
+ // Tests handling of locs with arrayed input patch seen in Tese
+ //
+ // Should report location {3}.
+ //
+ // #version 450 core
+ //
+ // layout(triangles, ccw) in;
+ //
+ // layout(fractional_odd_spacing) in;
+ //
+ // layout(point_mode) in;
+ //
+ // layout(location=2) patch in float patchIn1[2];
+ //
+ // void main()
+ // {
+ // vec4 p = gl_in[1].gl_Position;
+ // gl_Position = p * patchIn1[1];
+ // }
+ const std::string text = R"(
+ OpCapability Tessellation
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationEvaluation %main "main" %gl_in %_ %patchIn1
+ OpExecutionMode %main Triangles
+ OpExecutionMode %main SpacingFractionalOdd
+ OpExecutionMode %main VertexOrderCcw
+ OpExecutionMode %main PointMode
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %p "p"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpName %gl_in "gl_in"
+ OpName %gl_PerVertex_0 "gl_PerVertex"
+ OpMemberName %gl_PerVertex_0 0 "gl_Position"
+ OpName %_ ""
+ OpName %patchIn1 "patchIn1"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpDecorate %gl_PerVertex Block
+ OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+ OpDecorate %gl_PerVertex_0 Block
+ OpDecorate %patchIn1 Patch
+ OpDecorate %patchIn1 Location 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float
+ %uint_32 = OpConstant %uint 32
+%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32
+%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32
+ %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input
+ %int = OpTypeInt 32 1
+ %int_1 = OpConstant %int 1
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%gl_PerVertex_0 = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0
+ %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output
+ %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%_ptr_Input__arr_float_uint_2 = OpTypePointer Input %_arr_float_uint_2
+ %patchIn1 = OpVariable %_ptr_Input__arr_float_uint_2 Input
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %p = OpVariable %_ptr_Function_v4float Function
+ %22 = OpAccessChain %_ptr_Input_v4float %gl_in %int_1 %int_0
+ %23 = OpLoad %v4float %22
+ OpStore %p %23
+ %27 = OpLoad %v4float %p
+ %33 = OpAccessChain %_ptr_Input_float %patchIn1 %int_1
+ %34 = OpLoad %float %33
+ %35 = OpVectorTimesScalar %v4float %27 %34
+ %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %37 %35
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_inputs.find(0);
+ auto itr1 = live_inputs.find(1);
+ auto itr2 = live_inputs.find(2);
+ auto itr3 = live_inputs.find(3);
+
+ // Expect live_inputs == {3}
+ EXPECT_TRUE(itr0 == live_inputs.end());
+ EXPECT_TRUE(itr1 == live_inputs.end());
+ EXPECT_TRUE(itr2 == live_inputs.end());
+ EXPECT_TRUE(itr3 != live_inputs.end());
+}
+
+TEST_F(AnalyzeLiveInputTest, FragMultipleLocationsF16) {
+ // Should report locations {2, 5}
+ //
+ // #version 450
+ //
+ // layout(location = 2) in Vertex
+ // {
+ // f16vec4 color0;
+ // f16vec4 color1;
+ // f16vec4 color2[3];
+ // } iVert;
+ //
+ // layout(location = 0) out f16vec4 oFragColor;
+ //
+ // void main()
+ // {
+ // oFragColor = iVert.color0 + iVert.color2[1];
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ OpCapability Float16
+ OpCapability StorageInputOutput16
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %oFragColor %iVert
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %oFragColor "oFragColor"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "color0"
+ OpMemberName %Vertex 1 "color1"
+ OpMemberName %Vertex 2 "color2"
+ OpName %iVert "iVert"
+ OpDecorate %oFragColor Location 0
+ OpDecorate %Vertex Block
+ OpDecorate %iVert Location 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %half = OpTypeFloat 16
+ %v4half = OpTypeVector %half 4
+%_ptr_Output_v4half = OpTypePointer Output %v4half
+ %oFragColor = OpVariable %_ptr_Output_v4half Output
+ %uint = OpTypeInt 32 0
+ %uint_3 = OpConstant %uint 3
+%_arr_v4half_uint_3 = OpTypeArray %v4half %uint_3
+ %Vertex = OpTypeStruct %v4half %v4half %_arr_v4half_uint_3
+%_ptr_Input_Vertex = OpTypePointer Input %Vertex
+ %iVert = OpVariable %_ptr_Input_Vertex Input
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_Input_v4half = OpTypePointer Input %v4half
+ %int_2 = OpConstant %int 2
+ %int_1 = OpConstant %int 1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %19 = OpAccessChain %_ptr_Input_v4half %iVert %int_0
+ %20 = OpLoad %v4half %19
+ %23 = OpAccessChain %_ptr_Input_v4half %iVert %int_2 %int_1
+ %24 = OpLoad %v4half %23
+ %25 = OpFAdd %v4half %20 %24
+ OpStore %oFragColor %25
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ auto result = SinglePassRunToBinary<AnalyzeLiveInputPass>(
+ text, true, &live_inputs, &live_builtins);
+
+ auto itr0 = live_inputs.find(0);
+ auto itr1 = live_inputs.find(1);
+ auto itr2 = live_inputs.find(2);
+ auto itr3 = live_inputs.find(3);
+ auto itr4 = live_inputs.find(4);
+ auto itr5 = live_inputs.find(5);
+ auto itr6 = live_inputs.find(6);
+
+ // Expect live_inputs == {2, 5}
+ EXPECT_TRUE(itr0 == live_inputs.end());
+ EXPECT_TRUE(itr1 == live_inputs.end());
+ EXPECT_TRUE(itr2 != live_inputs.end());
+ EXPECT_TRUE(itr3 == live_inputs.end());
+ EXPECT_TRUE(itr4 == live_inputs.end());
+ EXPECT_TRUE(itr5 != live_inputs.end());
+ EXPECT_TRUE(itr6 == live_inputs.end());
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/test/opt/eliminate_dead_output_stores_test.cpp b/test/opt/eliminate_dead_output_stores_test.cpp
new file mode 100644
index 000000000..6e382c243
--- /dev/null
+++ b/test/opt/eliminate_dead_output_stores_test.cpp
@@ -0,0 +1,952 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 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 <unordered_set>
+
+#include "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using ElimDeadOutputStoresTest = PassTest<::testing::Test>;
+
+TEST_F(ElimDeadOutputStoresTest, VertMultipleLocations) {
+ // #version 450
+ //
+ // layout(location = 2) out Vertex
+ // {
+ // vec4 color0;
+ // vec4 color1;
+ // vec4 color2[3];
+ // } oVert;
+ //
+ // void main()
+ // {
+ // oVert.color0 = vec4(0.0,0.0,0.0,0.0);
+ // oVert.color1 = vec4(0.1,0.0,0.0,0.0);
+ // oVert.color2[0] = vec4(0.2,0.0,0.0,0.0);
+ // oVert.color2[1] = vec4(0.3,0.0,0.0,0.0);
+ // oVert.color2[2] = vec4(0.4,0.0,0.0,0.0);
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %oVert
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "color0"
+ OpMemberName %Vertex 1 "color1"
+ OpMemberName %Vertex 2 "color2"
+ OpName %oVert "oVert"
+ OpDecorate %Vertex Block
+ OpDecorate %oVert Location 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %uint = OpTypeInt 32 0
+ %uint_3 = OpConstant %uint 3
+%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
+ %Vertex = OpTypeStruct %v4float %v4float %_arr_v4float_uint_3
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+ %oVert = OpVariable %_ptr_Output_Vertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %float_0 = OpConstant %float 0
+ %17 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %int_1 = OpConstant %int 1
+%float_0_100000001 = OpConstant %float 0.100000001
+ %22 = OpConstantComposite %v4float %float_0_100000001 %float_0 %float_0 %float_0
+ %int_2 = OpConstant %int 2
+%float_0_200000003 = OpConstant %float 0.200000003
+ %26 = OpConstantComposite %v4float %float_0_200000003 %float_0 %float_0 %float_0
+%float_0_300000012 = OpConstant %float 0.300000012
+ %29 = OpConstantComposite %v4float %float_0_300000012 %float_0 %float_0 %float_0
+%float_0_400000006 = OpConstant %float 0.400000006
+ %32 = OpConstantComposite %v4float %float_0_400000006 %float_0 %float_0 %float_0
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %19 = OpAccessChain %_ptr_Output_v4float %oVert %int_0
+ OpStore %19 %17
+;CHECK: OpStore %19 %17
+ %23 = OpAccessChain %_ptr_Output_v4float %oVert %int_1
+ OpStore %23 %22
+;CHECK-NOT: OpStore %23 %22
+ %27 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_0
+ OpStore %27 %26
+;CHECK-NOT: OpStore %27 %26
+ %30 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_1
+ OpStore %30 %29
+;CHECK: OpStore %30 %29
+ %33 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_2
+ OpStore %33 %32
+;CHECK-NOT: OpStore %33 %32
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ live_inputs.insert(2);
+ live_inputs.insert(5);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, VertMatrix) {
+ // #version 450
+ //
+ // layout(location = 2) out Vertex
+ // {
+ // vec4 color0;
+ // vec4 color1;
+ // mat4 color2;
+ // mat4 color3;
+ // mat4 color4;
+ // } oVert;
+ //
+ // void main()
+ // {
+ // oVert.color0 = vec4(0.0,0.0,0.0,0.0);
+ // oVert.color1 = vec4(0.1,0.0,0.0,0.0);
+ // oVert.color2[2] = vec4(0.2,0.0,0.0,0.0);
+ // oVert.color3[1] = vec4(0.3,0.0,0.0,0.0);
+ // oVert.color4[0] = vec4(0.4,0.0,0.0,0.0);
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %oVert
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "color0"
+ OpMemberName %Vertex 1 "color1"
+ OpMemberName %Vertex 2 "color2"
+ OpMemberName %Vertex 3 "color3"
+ OpMemberName %Vertex 4 "color4"
+ OpName %oVert "oVert"
+ OpDecorate %Vertex Block
+ OpDecorate %oVert Location 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%mat4v4float = OpTypeMatrix %v4float 4
+ %Vertex = OpTypeStruct %v4float %v4float %mat4v4float %mat4v4float %mat4v4float
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+ %oVert = OpVariable %_ptr_Output_Vertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %float_0 = OpConstant %float 0
+ %15 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %int_1 = OpConstant %int 1
+%float_0_100000001 = OpConstant %float 0.100000001
+ %20 = OpConstantComposite %v4float %float_0_100000001 %float_0 %float_0 %float_0
+ %int_2 = OpConstant %int 2
+%float_0_200000003 = OpConstant %float 0.200000003
+ %24 = OpConstantComposite %v4float %float_0_200000003 %float_0 %float_0 %float_0
+ %int_3 = OpConstant %int 3
+%float_0_300000012 = OpConstant %float 0.300000012
+ %28 = OpConstantComposite %v4float %float_0_300000012 %float_0 %float_0 %float_0
+ %int_4 = OpConstant %int 4
+%float_0_400000006 = OpConstant %float 0.400000006
+ %32 = OpConstantComposite %v4float %float_0_400000006 %float_0 %float_0 %float_0
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %17 = OpAccessChain %_ptr_Output_v4float %oVert %int_0
+ OpStore %17 %15
+; CHECK: OpStore %17 %15
+ %21 = OpAccessChain %_ptr_Output_v4float %oVert %int_1
+ OpStore %21 %20
+; CHECK-NOT: OpStore %21 %20
+ %25 = OpAccessChain %_ptr_Output_v4float %oVert %int_2 %int_2
+ OpStore %25 %24
+; CHECK-NOT: OpStore %25 %24
+ %29 = OpAccessChain %_ptr_Output_v4float %oVert %int_3 %int_1
+ OpStore %29 %28
+; CHECK: OpStore %29 %28
+ %33 = OpAccessChain %_ptr_Output_v4float %oVert %int_4 %int_0
+ OpStore %33 %32
+; CHECK-NOT: OpStore %33 %32
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ live_inputs.insert(2);
+ live_inputs.insert(8);
+ live_inputs.insert(9);
+ live_inputs.insert(10);
+ live_inputs.insert(11);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, VertMemberLocs) {
+ // #version 450
+ //
+ // out Vertex
+ // {
+ // layout (location = 1) vec4 Cd;
+ // layout (location = 0) vec2 uv;
+ // } oVert;
+ //
+ // layout (location = 0) in vec3 P;
+ //
+ // void main()
+ // {
+ // oVert.uv = vec2(0.1, 0.7);
+ // oVert.Cd = vec4(1, 0.5, 0, 1);
+ // gl_Position = vec4(P, 1);
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %oVert %_ %P
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "Cd"
+ OpMemberName %Vertex 1 "uv"
+ OpName %oVert "oVert"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpMemberName %gl_PerVertex 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+ OpName %_ ""
+ OpName %P "P"
+ OpMemberDecorate %Vertex 0 Location 1
+ OpMemberDecorate %Vertex 1 Location 0
+ OpDecorate %Vertex Block
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+ OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+ OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %P Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %v2float = OpTypeVector %float 2
+ %Vertex = OpTypeStruct %v4float %v2float
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+ %oVert = OpVariable %_ptr_Output_Vertex Output
+ %int = OpTypeInt 32 1
+ %int_1 = OpConstant %int 1
+%float_0_100000001 = OpConstant %float 0.100000001
+%float_0_699999988 = OpConstant %float 0.699999988
+ %16 = OpConstantComposite %v2float %float_0_100000001 %float_0_699999988
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+ %int_0 = OpConstant %int 0
+ %float_1 = OpConstant %float 1
+ %float_0_5 = OpConstant %float 0.5
+ %float_0 = OpConstant %float 0
+ %23 = OpConstantComposite %v4float %float_1 %float_0_5 %float_0 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %v3float = OpTypeVector %float 3
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+ %P = OpVariable %_ptr_Input_v3float Input
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %18 = OpAccessChain %_ptr_Output_v2float %oVert %int_1
+ OpStore %18 %16
+; CHECK-NOT: OpStore %18 %16
+ %25 = OpAccessChain %_ptr_Output_v4float %oVert %int_0
+ OpStore %25 %23
+; CHECK: OpStore %25 %23
+ %35 = OpLoad %v3float %P
+ %36 = OpCompositeExtract %float %35 0
+ %37 = OpCompositeExtract %float %35 1
+ %38 = OpCompositeExtract %float %35 2
+ %39 = OpCompositeConstruct %v4float %36 %37 %38 %float_1
+ %40 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %40 %39
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ live_inputs.insert(1);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedOutput) {
+ // Tests elimination of arrayed output as seen in Tesc shaders.
+ //
+ // #version 450
+ //
+ // layout (vertices = 4) out;
+ //
+ // layout (location = 0) in vec3 N[];
+ // layout (location = 1) in vec3 P[];
+ //
+ // layout (location = 5) out Vertex
+ // {
+ // vec4 c;
+ // vec3 n;
+ // vec3 f[10];
+ // } oVert[];
+ //
+ // void main()
+ // {
+ // oVert[gl_InvocationID].c = vec4(1, 0, 0, 1);
+ // oVert[gl_InvocationID].n = N[gl_InvocationID];
+ // oVert[gl_InvocationID].f[3] = vec3(0, 1, 0);
+ // vec4 worldSpacePos = vec4(P[gl_InvocationID], 1);
+ // gl_out[gl_InvocationID].gl_Position = worldSpacePos;
+ // }
+ const std::string text = R"(
+ OpCapability Tessellation
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationControl %main "main" %oVert %gl_InvocationID %N %P %gl_out
+ OpExecutionMode %main OutputVertices 4
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "c"
+ OpMemberName %Vertex 1 "n"
+ OpMemberName %Vertex 2 "f"
+ OpName %oVert "oVert"
+ OpName %gl_InvocationID "gl_InvocationID"
+ OpName %N "N"
+ OpName %P "P"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpMemberName %gl_PerVertex 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+ OpName %gl_out "gl_out"
+ OpDecorate %Vertex Block
+ OpDecorate %oVert Location 5
+ OpDecorate %gl_InvocationID BuiltIn InvocationId
+ OpDecorate %N Location 0
+ OpDecorate %P Location 1
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+ OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+ OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+ OpDecorate %gl_PerVertex Block
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %v3float = OpTypeVector %float 3
+ %uint = OpTypeInt 32 0
+ %uint_10 = OpConstant %uint 10
+%_arr_v3float_uint_10 = OpTypeArray %v3float %uint_10
+ %Vertex = OpTypeStruct %v4float %v3float %_arr_v3float_uint_10
+ %uint_4 = OpConstant %uint 4
+%_arr_Vertex_uint_4 = OpTypeArray %Vertex %uint_4
+%_ptr_Output__arr_Vertex_uint_4 = OpTypePointer Output %_arr_Vertex_uint_4
+ %oVert = OpVariable %_ptr_Output__arr_Vertex_uint_4 Output
+ %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+ %int_0 = OpConstant %int 0
+ %float_1 = OpConstant %float 1
+ %float_0 = OpConstant %float 0
+ %24 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %int_1 = OpConstant %int 1
+ %uint_32 = OpConstant %uint 32
+%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32
+%_ptr_Input__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32
+ %N = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+ %int_2 = OpConstant %int 2
+ %int_3 = OpConstant %int 3
+ %42 = OpConstantComposite %v3float %float_0 %float_1 %float_0
+ %P = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+ %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_arr_gl_PerVertex_uint_4 = OpTypeArray %gl_PerVertex %uint_4
+%_ptr_Output__arr_gl_PerVertex_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_uint_4
+ %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_uint_4 Output
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpLoad %int %gl_InvocationID
+ %26 = OpAccessChain %_ptr_Output_v4float %oVert %20 %int_0
+ OpStore %26 %24
+; CHECK: OpStore %26 %24
+ %35 = OpAccessChain %_ptr_Input_v3float %N %20
+ %36 = OpLoad %v3float %35
+ %38 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_1
+ OpStore %38 %36
+; CHECK-NOT: OpStore %38 %36
+ %43 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_2 %int_3
+ OpStore %43 %42
+; CHECK: OpStore %43 %42
+ %48 = OpAccessChain %_ptr_Input_v3float %P %20
+ %49 = OpLoad %v3float %48
+ %50 = OpCompositeExtract %float %49 0
+ %51 = OpCompositeExtract %float %49 1
+ %52 = OpCompositeExtract %float %49 2
+ %53 = OpCompositeConstruct %v4float %50 %51 %52 %float_1
+ %62 = OpAccessChain %_ptr_Output_v4float %gl_out %20 %int_0
+ OpStore %62 %53
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ live_inputs.insert(5);
+ live_inputs.insert(10);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedOutputMemberLocs) {
+ // Tests elimination of member location with arrayed output as seen in
+ // Tesc shaders.
+ //
+ // #version 450
+ //
+ // layout (vertices = 4) out;
+ //
+ // layout (location = 0) in vec3 N[];
+ // layout (location = 1) in vec3 P[];
+ //
+ // out Vertex
+ // {
+ // layout (location = 1) vec4 c;
+ // layout (location = 3) vec3 n;
+ // layout (location = 5) vec3 f[10];
+ // } oVert[];
+ //
+ // void main()
+ // {
+ // oVert[gl_InvocationID].c = vec4(1, 0, 0, 1);
+ // oVert[gl_InvocationID].n = N[gl_InvocationID];
+ // oVert[gl_InvocationID].f[3] = vec3(0, 1, 0);
+ // vec4 worldSpacePos = vec4(P[gl_InvocationID], 1);
+ // gl_out[gl_InvocationID].gl_Position = worldSpacePos;
+ // }
+ const std::string text = R"(
+ OpCapability Tessellation
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationControl %main "main" %oVert %gl_InvocationID %N %P %gl_out
+ OpExecutionMode %main OutputVertices 4
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "c"
+ OpMemberName %Vertex 1 "n"
+ OpMemberName %Vertex 2 "f"
+ OpName %oVert "oVert"
+ OpName %gl_InvocationID "gl_InvocationID"
+ OpName %N "N"
+ OpName %P "P"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpMemberName %gl_PerVertex 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+ OpName %gl_out "gl_out"
+ OpMemberDecorate %Vertex 0 Location 1
+ OpMemberDecorate %Vertex 1 Location 3
+ OpMemberDecorate %Vertex 2 Location 5
+ OpDecorate %Vertex Block
+ OpDecorate %gl_InvocationID BuiltIn InvocationId
+ OpDecorate %N Location 0
+ OpDecorate %P Location 1
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+ OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+ OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+ OpDecorate %gl_PerVertex Block
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %v3float = OpTypeVector %float 3
+ %uint = OpTypeInt 32 0
+ %uint_10 = OpConstant %uint 10
+%_arr_v3float_uint_10 = OpTypeArray %v3float %uint_10
+ %Vertex = OpTypeStruct %v4float %v3float %_arr_v3float_uint_10
+ %uint_4 = OpConstant %uint 4
+%_arr_Vertex_uint_4 = OpTypeArray %Vertex %uint_4
+%_ptr_Output__arr_Vertex_uint_4 = OpTypePointer Output %_arr_Vertex_uint_4
+ %oVert = OpVariable %_ptr_Output__arr_Vertex_uint_4 Output
+ %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_InvocationID = OpVariable %_ptr_Input_int Input
+ %int_0 = OpConstant %int 0
+ %float_1 = OpConstant %float 1
+ %float_0 = OpConstant %float 0
+ %24 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %int_1 = OpConstant %int 1
+ %uint_32 = OpConstant %uint 32
+%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32
+%_ptr_Input__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32
+ %N = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+ %int_2 = OpConstant %int 2
+ %int_3 = OpConstant %int 3
+ %42 = OpConstantComposite %v3float %float_0 %float_1 %float_0
+ %P = OpVariable %_ptr_Input__arr_v3float_uint_32 Input
+ %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_arr_gl_PerVertex_uint_4 = OpTypeArray %gl_PerVertex %uint_4
+%_ptr_Output__arr_gl_PerVertex_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_uint_4
+ %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_uint_4 Output
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %20 = OpLoad %int %gl_InvocationID
+ %26 = OpAccessChain %_ptr_Output_v4float %oVert %20 %int_0
+ OpStore %26 %24
+;CHECK: OpStore %26 %24
+ %35 = OpAccessChain %_ptr_Input_v3float %N %20
+ %36 = OpLoad %v3float %35
+ %38 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_1
+ OpStore %38 %36
+;CHECK-NOT: OpStore %38 %36
+ %43 = OpAccessChain %_ptr_Output_v3float %oVert %20 %int_2 %int_3
+ OpStore %43 %42
+;CHECK: OpStore %43 %42
+ %48 = OpAccessChain %_ptr_Input_v3float %P %20
+ %49 = OpLoad %v3float %48
+ %50 = OpCompositeExtract %float %49 0
+ %51 = OpCompositeExtract %float %49 1
+ %52 = OpCompositeExtract %float %49 2
+ %53 = OpCompositeConstruct %v4float %50 %51 %52 %float_1
+ %62 = OpAccessChain %_ptr_Output_v4float %gl_out %20 %int_0
+ OpStore %62 %53
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ live_inputs.insert(1);
+ live_inputs.insert(8);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ScalarBuiltins) {
+ // Tests elimination of scalar builtins as seen in vert shaders.
+ //
+ // #version 460
+ //
+ // layout (location = 0) in vec3 P;
+ //
+ // void main()
+ // {
+ // gl_Position = vec4(P, 1.0);
+ // gl_PointSize = 1.0;
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %_ %P
+ OpSource GLSL 460
+ OpName %main "main"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpMemberName %gl_PerVertex 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+ OpName %_ ""
+ OpName %P "P"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+ OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+ OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+ OpDecorate %gl_PerVertex Block
+ OpDecorate %P Location 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %v3float = OpTypeVector %float 3
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+ %P = OpVariable %_ptr_Input_v3float Input
+ %float_1 = OpConstant %float 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %int_1 = OpConstant %int 1
+%_ptr_Output_float = OpTypePointer Output %float
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %19 = OpLoad %v3float %P
+ %21 = OpCompositeExtract %float %19 0
+ %22 = OpCompositeExtract %float %19 1
+ %23 = OpCompositeExtract %float %19 2
+ %24 = OpCompositeConstruct %v4float %21 %22 %23 %float_1
+ %26 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %26 %24
+;CHECK: OpStore %26 %24
+ %29 = OpAccessChain %_ptr_Output_float %_ %int_1
+ OpStore %29 %float_1
+;CHECK-NOT: OpStore %29 %float_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ // Omit SpvBuiltInPointSize
+ live_builtins.insert(SpvBuiltInClipDistance);
+ live_builtins.insert(SpvBuiltInCullDistance);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedBuiltins) {
+ // Tests elimination of arrayed builtins as seen in geom, tesc, and tese
+ // shaders.
+ //
+ // #version 460
+ //
+ // layout(triangle_strip, max_vertices = 3) out;
+ // layout(triangles) in;
+ //
+ // void main()
+ // {
+ // for (int i = 0; i < 3; i++)
+ // {
+ // gl_Position = gl_in[i].gl_Position;
+ // gl_PointSize = gl_in[i].gl_PointSize;
+ //
+ // EmitVertex();
+ // }
+ //
+ // EndPrimitive();
+ // }
+ const std::string text = R"(
+ OpCapability Geometry
+ OpCapability GeometryPointSize
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Geometry %main "main" %_ %gl_in
+ OpExecutionMode %main Triangles
+ OpExecutionMode %main Invocations 1
+ OpExecutionMode %main OutputTriangleStrip
+ OpExecutionMode %main OutputVertices 3
+ OpSource GLSL 460
+ OpName %main "main"
+ OpName %i "i"
+ OpName %gl_PerVertex "gl_PerVertex"
+ OpMemberName %gl_PerVertex 0 "gl_Position"
+ OpMemberName %gl_PerVertex 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex 3 "gl_CullDistance"
+ OpName %_ ""
+ OpName %gl_PerVertex_0 "gl_PerVertex"
+ OpMemberName %gl_PerVertex_0 0 "gl_Position"
+ OpMemberName %gl_PerVertex_0 1 "gl_PointSize"
+ OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance"
+ OpMemberName %gl_PerVertex_0 3 "gl_CullDistance"
+ OpName %gl_in "gl_in"
+ OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize
+ OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance
+ OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance
+ OpDecorate %gl_PerVertex Block
+ OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position
+ OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize
+ OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance
+ OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance
+ OpDecorate %gl_PerVertex_0 Block
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+ %int_0 = OpConstant %int 0
+ %int_3 = OpConstant %int 3
+ %bool = OpTypeBool
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+ %uint = OpTypeInt 32 0
+ %uint_1 = OpConstant %uint 1
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+ %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+ %uint_3 = OpConstant %uint 3
+%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3
+%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3
+ %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %int_1 = OpConstant %int 1
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_float = OpTypePointer Output %float
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %i = OpVariable %_ptr_Function_int Function
+ OpStore %i %int_0
+ OpBranch %10
+ %10 = OpLabel
+ OpLoopMerge %12 %13 None
+ OpBranch %14
+ %14 = OpLabel
+ %15 = OpLoad %int %i
+ %18 = OpSLessThan %bool %15 %int_3
+ OpBranchConditional %18 %11 %12
+ %11 = OpLabel
+ %32 = OpLoad %int %i
+ %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0
+ %35 = OpLoad %v4float %34
+ %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+ OpStore %37 %35
+;CHECK: OpStore %37 %35
+ %39 = OpLoad %int %i
+ %41 = OpAccessChain %_ptr_Input_float %gl_in %39 %int_1
+ %42 = OpLoad %float %41
+ %44 = OpAccessChain %_ptr_Output_float %_ %int_1
+ OpStore %44 %42
+;CHECK-NOT: OpStore %44 %42
+ OpEmitVertex
+ OpBranch %13
+ %13 = OpLabel
+ %45 = OpLoad %int %i
+ %46 = OpIAdd %int %45 %int_1
+ OpStore %i %46
+ OpBranch %10
+ %12 = OpLabel
+ OpEndPrimitive
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ // Omit SpvBuiltInPointSize
+ live_builtins.insert(SpvBuiltInClipDistance);
+ live_builtins.insert(SpvBuiltInCullDistance);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, ArrayedOutputPatchLocs) {
+ // Tests elimination of location with arrayed patch output as seen in
+ // Tesc shaders.
+ //
+ // #version 450 core
+ //
+ // layout(vertices = 4) out;
+ //
+ // layout(location=0) patch out float patchOut0[2];
+ // layout(location=2) patch out float patchOut1[2];
+ //
+ // void main()
+ // {
+ // patchOut0[1] = 0.0; // Dead loc 1
+ // patchOut1[1] = 1.0; // Live loc 3
+ // }
+ const std::string text = R"(
+ OpCapability Tessellation
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint TessellationControl %main "main" %patchOut0 %patchOut1
+ OpExecutionMode %main OutputVertices 4
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %patchOut0 "patchOut0"
+ OpName %patchOut1 "patchOut1"
+ OpDecorate %patchOut0 Patch
+ OpDecorate %patchOut0 Location 0
+ OpDecorate %patchOut1 Patch
+ OpDecorate %patchOut1 Location 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+ %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2
+ %patchOut0 = OpVariable %_ptr_Output__arr_float_uint_2 Output
+ %int = OpTypeInt 32 1
+ %int_1 = OpConstant %int 1
+ %float_0 = OpConstant %float 0
+%_ptr_Output_float = OpTypePointer Output %float
+ %patchOut1 = OpVariable %_ptr_Output__arr_float_uint_2 Output
+ %float_1 = OpConstant %float 1
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %16 = OpAccessChain %_ptr_Output_float %patchOut0 %int_1
+ OpStore %16 %float_0
+;CHECK-NOT: OpStore %16 %float_0
+ %19 = OpAccessChain %_ptr_Output_float %patchOut1 %int_1
+ OpStore %19 %float_1
+;CHECK: OpStore %19 %float_1
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ live_inputs.insert(3);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+TEST_F(ElimDeadOutputStoresTest, VertMultipleLocationsF16) {
+ // #version 450
+ //
+ // layout(location = 2) out Vertex
+ // {
+ // f16vec4 color0;
+ // f16vec4 color1;
+ // f16vec4 color2[3];
+ // } oVert;
+ //
+ // void main()
+ // {
+ // oVert.color0 = f16vec4(0.0,0.0,0.0,0.0);
+ // oVert.color1 = f16vec4(0.1,0.0,0.0,0.0);
+ // oVert.color2[0] = f16vec4(0.2,0.0,0.0,0.0);
+ // oVert.color2[1] = f16vec4(0.3,0.0,0.0,0.0);
+ // oVert.color2[2] = f16vec4(0.4,0.0,0.0,0.0);
+ // }
+ const std::string text = R"(
+ OpCapability Shader
+ OpCapability Float16
+ OpCapability StorageInputOutput16
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %oVert
+ OpSource GLSL 450
+ OpName %main "main"
+ OpName %Vertex "Vertex"
+ OpMemberName %Vertex 0 "color0"
+ OpMemberName %Vertex 1 "color1"
+ OpMemberName %Vertex 2 "color2"
+ OpName %oVert "oVert"
+ OpDecorate %Vertex Block
+ OpDecorate %oVert Location 2
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %half = OpTypeFloat 32
+ %v4half = OpTypeVector %half 4
+ %uint = OpTypeInt 32 0
+ %uint_3 = OpConstant %uint 3
+%_arr_v4half_uint_3 = OpTypeArray %v4half %uint_3
+ %Vertex = OpTypeStruct %v4half %v4half %_arr_v4half_uint_3
+%_ptr_Output_Vertex = OpTypePointer Output %Vertex
+ %oVert = OpVariable %_ptr_Output_Vertex Output
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %half_0 = OpConstant %half 0
+ %17 = OpConstantComposite %v4half %half_0 %half_0 %half_0 %half_0
+%_ptr_Output_v4half = OpTypePointer Output %v4half
+ %int_1 = OpConstant %int 1
+%half_0_100000001 = OpConstant %half 0.100000001
+ %22 = OpConstantComposite %v4half %half_0_100000001 %half_0 %half_0 %half_0
+ %int_2 = OpConstant %int 2
+%half_0_200000003 = OpConstant %half 0.200000003
+ %26 = OpConstantComposite %v4half %half_0_200000003 %half_0 %half_0 %half_0
+%half_0_300000012 = OpConstant %half 0.300000012
+ %29 = OpConstantComposite %v4half %half_0_300000012 %half_0 %half_0 %half_0
+%half_0_400000006 = OpConstant %half 0.400000006
+ %32 = OpConstantComposite %v4half %half_0_400000006 %half_0 %half_0 %half_0
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %19 = OpAccessChain %_ptr_Output_v4half %oVert %int_0
+ OpStore %19 %17
+;CHECK: OpStore %19 %17
+ %23 = OpAccessChain %_ptr_Output_v4half %oVert %int_1
+ OpStore %23 %22
+;CHECK-NOT: OpStore %23 %22
+ %27 = OpAccessChain %_ptr_Output_v4half %oVert %int_2 %int_0
+ OpStore %27 %26
+;CHECK-NOT: OpStore %27 %26
+ %30 = OpAccessChain %_ptr_Output_v4half %oVert %int_2 %int_1
+ OpStore %30 %29
+;CHECK: OpStore %30 %29
+ %33 = OpAccessChain %_ptr_Output_v4half %oVert %int_2 %int_2
+ OpStore %33 %32
+;CHECK-NOT: OpStore %33 %32
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_VULKAN_1_3);
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+ std::unordered_set<uint32_t> live_inputs;
+ std::unordered_set<uint32_t> live_builtins;
+ live_inputs.insert(2);
+ live_inputs.insert(5);
+ SinglePassRunAndMatch<EliminateDeadOutputStoresPass>(text, true, &live_inputs,
+ &live_builtins);
+}
+
+} // namespace
+} // namespace opt
+} // namespace spvtools