| // Copyright (c) 2019 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "assembly_builder.h" |
| #include "gmock/gmock.h" |
| #include "pass_fixture.h" |
| #include "pass_utils.h" |
| |
| namespace { |
| |
| using namespace spvtools; |
| |
| using EliminateDeadMemberTest = opt::PassTest<::testing::Test>; |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMember1) { |
| // Test that the member "y" is removed. |
| // Update OpMemberName for |y| and |z|. |
| // Update OpMemberDecorate for |y| and |z|. |
| // Update OpAccessChain for access to |z|. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "x" |
| ; CHECK-NEXT: OpMemberName %type__Globals 1 "z" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 |
| ; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 |
| ; CHECK: %type__Globals = OpTypeStruct %float %float |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %int_0 |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %in_var_Position "in.var.Position" |
| OpName %main "main" |
| OpDecorate %gl_Position BuiltIn Position |
| OpDecorate %in_var_Position Location 0 |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 8 |
| OpDecorate %type__Globals Block |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %float = OpTypeFloat 32 |
| %int_2 = OpConstant %int 2 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %void = OpTypeVoid |
| %15 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %in_var_Position = OpVariable %_ptr_Input_v4float Input |
| %gl_Position = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %15 |
| %17 = OpLabel |
| %18 = OpLoad %v4float %in_var_Position |
| %19 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 |
| %20 = OpLoad %float %19 |
| %21 = OpCompositeExtract %float %18 0 |
| %22 = OpFAdd %float %21 %20 |
| %23 = OpCompositeInsert %v4float %22 %18 0 |
| %24 = OpCompositeExtract %float %18 1 |
| %25 = OpCompositeInsert %v4float %24 %23 1 |
| %26 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 |
| %27 = OpLoad %float %26 |
| %28 = OpCompositeExtract %float %18 2 |
| %29 = OpFAdd %float %28 %27 |
| %30 = OpCompositeInsert %v4float %29 %25 2 |
| OpStore %gl_Position %30 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMemberWithGroupDecorations) { |
| // Test that the member "y" is removed. |
| // Update OpGroupMemberDecorate for %type__Globals member 1 and 2. |
| // Update OpAccessChain for access to %type__Globals member 2. |
| const std::string text = R"( |
| ; CHECK: OpDecorate [[gr1:%\w+]] Offset 0 |
| ; CHECK: OpDecorate [[gr2:%\w+]] Offset 4 |
| ; CHECK: OpDecorate [[gr3:%\w+]] Offset 8 |
| ; CHECK: [[gr1]] = OpDecorationGroup |
| ; CHECK: [[gr2]] = OpDecorationGroup |
| ; CHECK: [[gr3]] = OpDecorationGroup |
| ; CHECK: OpGroupMemberDecorate [[gr1]] %type__Globals 0 |
| ; CHECK-NOT: OpGroupMemberDecorate [[gr2]] |
| ; CHECK: OpGroupMemberDecorate [[gr3]] %type__Globals 1 |
| ; CHECK: %type__Globals = OpTypeStruct %float %float |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %int_0 |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpName %_Globals "$Globals" |
| OpDecorate %gl_Position BuiltIn Position |
| OpDecorate %in_var_Position Location 0 |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpDecorate %gr1 Offset 0 |
| OpDecorate %gr2 Offset 4 |
| OpDecorate %gr3 Offset 8 |
| OpDecorate %type__Globals Block |
| %gr1 = OpDecorationGroup |
| %gr2 = OpDecorationGroup |
| %gr3 = OpDecorationGroup |
| OpGroupMemberDecorate %gr1 %type__Globals 0 |
| OpGroupMemberDecorate %gr2 %type__Globals 1 |
| OpGroupMemberDecorate %gr3 %type__Globals 2 |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %float = OpTypeFloat 32 |
| %int_2 = OpConstant %int 2 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %void = OpTypeVoid |
| %15 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %in_var_Position = OpVariable %_ptr_Input_v4float Input |
| %gl_Position = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %15 |
| %17 = OpLabel |
| %18 = OpLoad %v4float %in_var_Position |
| %19 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 |
| %20 = OpLoad %float %19 |
| %21 = OpCompositeExtract %float %18 0 |
| %22 = OpFAdd %float %21 %20 |
| %23 = OpCompositeInsert %v4float %22 %18 0 |
| %24 = OpCompositeExtract %float %18 1 |
| %25 = OpCompositeInsert %v4float %24 %23 1 |
| %26 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 |
| %27 = OpLoad %float %26 |
| %28 = OpCompositeExtract %float %18 2 |
| %29 = OpFAdd %float %28 %27 |
| %30 = OpCompositeInsert %v4float %29 %25 2 |
| OpStore %gl_Position %30 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| // Skipping validation because of a bug in the validator. See issue #2376. |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, false); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMemberUpdateConstant) { |
| // Test that the member "x" is removed. |
| // Update the OpConstantComposite instruction. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NEXT: OpMemberName %type__Globals 1 "z" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 |
| ; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 |
| ; CHECK: %type__Globals = OpTypeStruct %float %float |
| ; CHECK: OpConstantComposite %type__Globals %float_1 %float_2 |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_0 |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %in_var_Position "in.var.Position" |
| OpName %main "main" |
| OpDecorate %gl_Position BuiltIn Position |
| OpDecorate %in_var_Position Location 0 |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 8 |
| OpDecorate %type__Globals Block |
| %int = OpTypeInt 32 1 |
| %int_1 = OpConstant %int 1 |
| %float = OpTypeFloat 32 |
| %float_0 = OpConstant %float 0 |
| %float_1 = OpConstant %float 1 |
| %float_2 = OpConstant %float 2 |
| %int_2 = OpConstant %int 2 |
| %type__Globals = OpTypeStruct %float %float %float |
| %13 = OpConstantComposite %type__Globals %float_0 %float_1 %float_2 |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %void = OpTypeVoid |
| %19 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %in_var_Position = OpVariable %_ptr_Input_v4float Input |
| %gl_Position = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %19 |
| %21 = OpLabel |
| %22 = OpLoad %v4float %in_var_Position |
| %23 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 |
| %24 = OpLoad %float %23 |
| %25 = OpCompositeExtract %float %22 0 |
| %26 = OpFAdd %float %25 %24 |
| %27 = OpCompositeInsert %v4float %26 %22 0 |
| %28 = OpCompositeExtract %float %22 1 |
| %29 = OpCompositeInsert %v4float %28 %27 1 |
| %30 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 |
| %31 = OpLoad %float %30 |
| %32 = OpCompositeExtract %float %22 2 |
| %33 = OpFAdd %float %32 %31 |
| %34 = OpCompositeInsert %v4float %33 %29 2 |
| OpStore %gl_Position %34 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMemberUpdateCompositeConstruct) { |
| // Test that the member "x" is removed. |
| // Update the OpConstantComposite instruction. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NEXT: OpMemberName %type__Globals 1 "z" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 |
| ; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 |
| ; CHECK: %type__Globals = OpTypeStruct %float %float |
| ; CHECK: OpCompositeConstruct %type__Globals %float_1 %float_2 |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_0 |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %in_var_Position "in.var.Position" |
| OpName %main "main" |
| OpDecorate %gl_Position BuiltIn Position |
| OpDecorate %in_var_Position Location 0 |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 8 |
| OpDecorate %type__Globals Block |
| %int = OpTypeInt 32 1 |
| %int_1 = OpConstant %int 1 |
| %float = OpTypeFloat 32 |
| %float_0 = OpConstant %float 0 |
| %float_1 = OpConstant %float 1 |
| %float_2 = OpConstant %float 2 |
| %int_2 = OpConstant %int 2 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %void = OpTypeVoid |
| %19 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %in_var_Position = OpVariable %_ptr_Input_v4float Input |
| %gl_Position = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %19 |
| %21 = OpLabel |
| %13 = OpCompositeConstruct %type__Globals %float_0 %float_1 %float_2 |
| %22 = OpLoad %v4float %in_var_Position |
| %23 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 |
| %24 = OpLoad %float %23 |
| %25 = OpCompositeExtract %float %22 0 |
| %26 = OpFAdd %float %25 %24 |
| %27 = OpCompositeInsert %v4float %26 %22 0 |
| %28 = OpCompositeExtract %float %22 1 |
| %29 = OpCompositeInsert %v4float %28 %27 1 |
| %30 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 |
| %31 = OpLoad %float %30 |
| %32 = OpCompositeExtract %float %22 2 |
| %33 = OpFAdd %float %32 %31 |
| %34 = OpCompositeInsert %v4float %33 %29 2 |
| OpStore %gl_Position %34 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract1) { |
| // Test that the members "x" and "z" are removed. |
| // Update the OpCompositeExtract instruction. |
| // Remove the OpCompositeInsert instruction since the member being inserted is |
| // dead. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 |
| ; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset |
| ; CHECK: %type__Globals = OpTypeStruct %float |
| ; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals |
| ; CHECK: OpCompositeExtract %float [[ld]] 0 |
| ; CHECK-NOT: OpCompositeInsert |
| ; CHECK: OpReturn |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 8 |
| OpDecorate %type__Globals Block |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %7 |
| %8 = OpLabel |
| %9 = OpLoad %type__Globals %_Globals |
| %10 = OpCompositeExtract %float %9 1 |
| %11 = OpCompositeInsert %type__Globals %10 %9 2 |
| OpReturn |
| OpFunctionEnd |
| |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract2) { |
| // Test that the members "x" and "z" are removed. |
| // Update the OpCompositeExtract instruction. |
| // Update the OpCompositeInsert instruction. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 |
| ; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset |
| ; CHECK: %type__Globals = OpTypeStruct %float |
| ; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals |
| ; CHECK: [[ex:%\w+]] = OpCompositeExtract %float [[ld]] 0 |
| ; CHECK: OpCompositeInsert %type__Globals [[ex]] [[ld]] 0 |
| ; CHECK: OpReturn |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 8 |
| OpDecorate %type__Globals Block |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %7 |
| %8 = OpLabel |
| %9 = OpLoad %type__Globals %_Globals |
| %10 = OpCompositeExtract %float %9 1 |
| %11 = OpCompositeInsert %type__Globals %10 %9 1 |
| OpReturn |
| OpFunctionEnd |
| |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract3) { |
| // Test that the members "x" and "z" are removed, and one member from the |
| // substruct. Update the OpCompositeExtract instruction. Update the |
| // OpCompositeInsert instruction. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 |
| ; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset |
| ; CHECK: OpMemberDecorate [[struct:%\w+]] 0 Offset 4 |
| ; CHECK: [[struct:%\w+]] = OpTypeStruct %float |
| ; CHECK: %type__Globals = OpTypeStruct [[struct]] |
| ; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals |
| ; CHECK: [[ex:%\w+]] = OpCompositeExtract %float [[ld]] 0 0 |
| ; CHECK: OpCompositeInsert %type__Globals [[ex]] [[ld]] 0 0 |
| ; CHECK: OpReturn |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 16 |
| OpMemberDecorate %type__Globals 2 Offset 24 |
| OpMemberDecorate %_struct_6 0 Offset 0 |
| OpMemberDecorate %_struct_6 1 Offset 4 |
| OpDecorate %type__Globals Block |
| %float = OpTypeFloat 32 |
| %_struct_6 = OpTypeStruct %float %float |
| %type__Globals = OpTypeStruct %float %_struct_6 %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %7 |
| %8 = OpLabel |
| %9 = OpLoad %type__Globals %_Globals |
| %10 = OpCompositeExtract %float %9 1 1 |
| %11 = OpCompositeInsert %type__Globals %10 %9 1 1 |
| OpReturn |
| OpFunctionEnd |
| |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract4) { |
| // Test that the members "x" and "z" are removed, and one member from the |
| // substruct. Update the OpCompositeExtract instruction. Update the |
| // OpCompositeInsert instruction. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 |
| ; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset |
| ; CHECK: OpMemberDecorate [[struct:%\w+]] 0 Offset 4 |
| ; CHECK: [[struct:%\w+]] = OpTypeStruct %float |
| ; CHECK: [[array:%\w+]] = OpTypeArray [[struct]] |
| ; CHECK: %type__Globals = OpTypeStruct [[array]] |
| ; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals |
| ; CHECK: [[ex:%\w+]] = OpCompositeExtract %float [[ld]] 0 1 0 |
| ; CHECK: OpCompositeInsert %type__Globals [[ex]] [[ld]] 0 1 0 |
| ; CHECK: OpReturn |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 16 |
| OpMemberDecorate %type__Globals 2 Offset 80 |
| OpMemberDecorate %_struct_6 0 Offset 0 |
| OpMemberDecorate %_struct_6 1 Offset 4 |
| OpDecorate %array ArrayStride 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 ; 32-bit int, sign-less |
| %uint_4 = OpConstant %uint 4 |
| %float = OpTypeFloat 32 |
| %_struct_6 = OpTypeStruct %float %float |
| %array = OpTypeArray %_struct_6 %uint_4 |
| %type__Globals = OpTypeStruct %float %array %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %7 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %7 |
| %8 = OpLabel |
| %9 = OpLoad %type__Globals %_Globals |
| %10 = OpCompositeExtract %float %9 1 1 1 |
| %11 = OpCompositeInsert %type__Globals %10 %9 1 1 1 |
| OpReturn |
| OpFunctionEnd |
| |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateArrayLength) { |
| // Test that the members "x" and "y" are removed. |
| // Member "z" is live because of the OpArrayLength instruction. |
| // Update the OpArrayLength instruction. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "z" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 |
| ; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset |
| ; CHECK: %type__Globals = OpTypeStruct %_runtimearr_float |
| ; CHECK: OpArrayLength %uint %_Globals 0 |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpDecorate %_runtimearr_float ArrayStride 16 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 |
| %float = OpTypeFloat 32 |
| %_runtimearr_float = OpTypeRuntimeArray %float |
| %type__Globals = OpTypeStruct %float %float %_runtimearr_float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %9 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %9 |
| %10 = OpLabel |
| %12 = OpArrayLength %uint %_Globals 2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, KeepMembersOpStore) { |
| // Test that all members are kept because of an OpStore. |
| // No change expected. |
| const std::string text = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %_Globals "$Globals2" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %9 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %9 |
| %10 = OpLabel |
| %11 = OpLoad %type__Globals %_Globals |
| OpStore %_Globals2 %11 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, KeepStorageBufferMembers) { |
| // Test that all members of the storage buffer struct %S are kept. |
| // No change expected. |
| const std::string text = R"( |
| OpCapability Shader |
| OpExtension "SPV_GOOGLE_hlsl_functionality1" |
| OpExtension "SPV_GOOGLE_user_type" |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %PSMain "PSMain" %out_var_SV_TARGET |
| OpExecutionMode %PSMain OriginUpperLeft |
| OpSource HLSL 600 |
| OpName %type_StructuredBuffer_S "type.StructuredBuffer.S" |
| OpName %S "S" |
| OpMemberName %S 0 "A" |
| OpMemberName %S 1 "B" |
| OpName %Buf "Buf" |
| OpName %out_var_SV_TARGET "out.var.SV_TARGET" |
| OpName %PSMain "PSMain" |
| OpDecorateString %out_var_SV_TARGET UserSemantic "SV_TARGET" |
| OpDecorate %out_var_SV_TARGET Location 0 |
| OpDecorate %Buf DescriptorSet 0 |
| OpDecorate %Buf Binding 0 |
| OpMemberDecorate %S 0 Offset 0 |
| OpMemberDecorate %S 1 Offset 16 |
| OpDecorate %_runtimearr_S ArrayStride 32 |
| OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0 |
| OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable |
| OpDecorate %type_StructuredBuffer_S BufferBlock |
| OpDecorateString %Buf UserTypeGOOGLE "structuredbuffer" |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %int_1 = OpConstant %int 1 |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %S = OpTypeStruct %v4float %v4float |
| %_runtimearr_S = OpTypeRuntimeArray %S |
| %type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S |
| %_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %void = OpTypeVoid |
| %18 = OpTypeFunction %void |
| %_ptr_Uniform_v4float = OpTypePointer Uniform %v4float |
| %Buf = OpVariable %_ptr_Uniform_type_StructuredBuffer_S Uniform |
| %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output |
| %PSMain = OpFunction %void None %18 |
| %20 = OpLabel |
| %21 = OpAccessChain %_ptr_Uniform_v4float %Buf %int_0 %uint_0 %int_1 |
| %22 = OpLoad %v4float %21 |
| OpStore %out_var_SV_TARGET %22 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemory) { |
| // Test that all members are kept because of an OpCopyMemory. |
| // No change expected. |
| const std::string text = R"( |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %_Globals "$Globals2" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %9 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %9 |
| %10 = OpLabel |
| OpCopyMemory %_Globals2 %_Globals |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemorySized) { |
| // Test that all members are kept because of an OpCopyMemorySized. |
| // No change expected. |
| const std::string text = R"( |
| OpCapability Shader |
| OpCapability Addresses |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %_Globals "$Globals2" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 |
| %uint_20 = OpConstant %uint 20 |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %9 = OpTypeFunction %void |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %void None %9 |
| %10 = OpLabel |
| OpCopyMemorySized %_Globals2 %_Globals %uint_20 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, KeepMembersOpReturnValue) { |
| // Test that all members are kept because of an OpCopyMemorySized. |
| // No change expected. |
| const std::string text = R"( |
| OpCapability Shader |
| OpCapability Linkage |
| OpMemoryModel Logical GLSL450 |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %_Globals "$Globals2" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 |
| %uint_20 = OpConstant %uint 20 |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %void = OpTypeVoid |
| %9 = OpTypeFunction %type__Globals |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %main = OpFunction %type__Globals None %9 |
| %10 = OpLabel |
| %11 = OpLoad %type__Globals %_Globals |
| OpReturnValue %11 |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMemberAccessChainWithArrays) { |
| // Leave only 1 member in each of the structs. |
| // Update OpMemberName, OpMemberDecorate, and OpAccessChain. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 |
| ; CHECK: OpMemberDecorate [[struct:%\w+]] 0 Offset 4 |
| ; CHECK: [[struct]] = OpTypeStruct %float |
| ; CHECK: [[array:%\w+]] = OpTypeArray [[struct]] |
| ; CHECK: %type__Globals = OpTypeStruct [[array]] |
| ; CHECK: [[undef:%\w+]] = OpUndef %uint |
| ; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals [[undef]] %uint_0 [[undef]] %uint_0 |
| OpCapability Shader |
| OpCapability VariablePointersStorageBuffer |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 16 |
| OpMemberDecorate %type__Globals 2 Offset 48 |
| OpMemberDecorate %_struct_4 0 Offset 0 |
| OpMemberDecorate %_struct_4 1 Offset 4 |
| OpDecorate %_arr__struct_4_uint_2 ArrayStride 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_2 = OpConstant %uint 2 |
| %uint_3 = OpConstant %uint 3 |
| %float = OpTypeFloat 32 |
| %_struct_4 = OpTypeStruct %float %float |
| %_arr__struct_4_uint_2 = OpTypeArray %_struct_4 %uint_2 |
| %type__Globals = OpTypeStruct %float %_arr__struct_4_uint_2 %float |
| %_arr_type__Globals_uint_3 = OpTypeArray %type__Globals %uint_3 |
| %_ptr_Uniform__arr_type__Globals_uint_3 = OpTypePointer Uniform %_arr_type__Globals_uint_3 |
| %void = OpTypeVoid |
| %15 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform__arr_type__Globals_uint_3 Uniform |
| %main = OpFunction %void None %15 |
| %17 = OpLabel |
| %18 = OpUndef %uint |
| %19 = OpAccessChain %_ptr_Uniform_float %_Globals %18 %uint_1 %18 %uint_1 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMemberInboundsAccessChain) { |
| // Test that the member "y" is removed. |
| // Update OpMemberName for |y| and |z|. |
| // Update OpMemberDecorate for |y| and |z|. |
| // Update OpInboundsAccessChain for access to |z|. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "x" |
| ; CHECK-NEXT: OpMemberName %type__Globals 1 "z" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 |
| ; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 |
| ; CHECK: %type__Globals = OpTypeStruct %float %float |
| ; CHECK: OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %int_0 |
| ; CHECK: OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %uint_1 |
| OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %in_var_Position "in.var.Position" |
| OpName %main "main" |
| OpDecorate %gl_Position BuiltIn Position |
| OpDecorate %in_var_Position Location 0 |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 8 |
| OpDecorate %type__Globals Block |
| %int = OpTypeInt 32 1 |
| %int_0 = OpConstant %int 0 |
| %float = OpTypeFloat 32 |
| %int_2 = OpConstant %int 2 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %v4float = OpTypeVector %float 4 |
| %_ptr_Input_v4float = OpTypePointer Input %v4float |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %void = OpTypeVoid |
| %15 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform |
| %in_var_Position = OpVariable %_ptr_Input_v4float Input |
| %gl_Position = OpVariable %_ptr_Output_v4float Output |
| %main = OpFunction %void None %15 |
| %17 = OpLabel |
| %18 = OpLoad %v4float %in_var_Position |
| %19 = OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %int_0 |
| %20 = OpLoad %float %19 |
| %21 = OpCompositeExtract %float %18 0 |
| %22 = OpFAdd %float %21 %20 |
| %23 = OpCompositeInsert %v4float %22 %18 0 |
| %24 = OpCompositeExtract %float %18 1 |
| %25 = OpCompositeInsert %v4float %24 %23 1 |
| %26 = OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %int_2 |
| %27 = OpLoad %float %26 |
| %28 = OpCompositeExtract %float %18 2 |
| %29 = OpFAdd %float %28 %27 |
| %30 = OpCompositeInsert %v4float %29 %25 2 |
| OpStore %gl_Position %30 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMemberPtrAccessChain) { |
| // Test that the member "y" is removed. |
| // Update OpMemberName for |y| and |z|. |
| // Update OpMemberDecorate for |y| and |z|. |
| // Update OpInboundsAccessChain for access to |z|. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "x" |
| ; CHECK-NEXT: OpMemberName %type__Globals 1 "z" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 |
| ; CHECK: OpMemberDecorate %type__Globals 1 Offset 16 |
| ; CHECK: %type__Globals = OpTypeStruct %float %float |
| ; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 |
| ; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_1 %uint_0 |
| ; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_1 |
| OpCapability Shader |
| OpCapability VariablePointersStorageBuffer |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| OpDecorate %type__Globals Block |
| OpDecorate %_ptr_Uniform_type__Globals ArrayStride 8 |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_2 = OpConstant %uint 2 |
| %uint_3 = OpConstant %uint 3 |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_arr_type__Globals_uint_3 = OpTypeArray %type__Globals %uint_3 |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %_ptr_Uniform__arr_type__Globals_uint_3 = OpTypePointer Uniform %_arr_type__Globals_uint_3 |
| %void = OpTypeVoid |
| %14 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform__arr_type__Globals_uint_3 Uniform |
| %main = OpFunction %void None %14 |
| %16 = OpLabel |
| %17 = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 |
| %18 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_1 %uint_0 |
| %19 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, RemoveMemberInBoundsPtrAccessChain) { |
| // Test that the member "y" is removed. |
| // Update OpMemberName for |y| and |z|. |
| // Update OpMemberDecorate for |y| and |z|. |
| // Update OpInboundsAccessChain for access to |z|. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "x" |
| ; CHECK-NEXT: OpMemberName %type__Globals 1 "z" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 |
| ; CHECK: OpMemberDecorate %type__Globals 1 Offset 16 |
| ; CHECK: %type__Globals = OpTypeStruct %float %float |
| ; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 |
| ; CHECK: OpInBoundsPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_1 %uint_0 |
| ; CHECK: OpInBoundsPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_1 |
| OpCapability Shader |
| OpCapability Addresses |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %_Globals "$Globals" |
| OpName %main "main" |
| OpDecorate %_Globals DescriptorSet 0 |
| OpDecorate %_Globals Binding 0 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| OpDecorate %type__Globals Block |
| %uint = OpTypeInt 32 0 |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_2 = OpConstant %uint 2 |
| %uint_3 = OpConstant %uint 3 |
| %float = OpTypeFloat 32 |
| %type__Globals = OpTypeStruct %float %float %float |
| %_arr_type__Globals_uint_3 = OpTypeArray %type__Globals %uint_3 |
| %_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals |
| %_ptr_Uniform__arr_type__Globals_uint_3 = OpTypePointer Uniform %_arr_type__Globals_uint_3 |
| %void = OpTypeVoid |
| %14 = OpTypeFunction %void |
| %_ptr_Uniform_float = OpTypePointer Uniform %float |
| %_Globals = OpVariable %_ptr_Uniform__arr_type__Globals_uint_3 Uniform |
| %main = OpFunction %void None %14 |
| %16 = OpLabel |
| %17 = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 |
| %18 = OpInBoundsPtrAccessChain %_ptr_Uniform_float %17 %uint_1 %uint_0 |
| %19 = OpInBoundsPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_2 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, DontRemoveModfStructResultTypeMembers) { |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 600 |
| %float = OpTypeFloat 32 |
| %void = OpTypeVoid |
| %21 = OpTypeFunction %void |
| %ModfStructType = OpTypeStruct %float %float |
| %main = OpFunction %void None %21 |
| %22 = OpLabel |
| %23 = OpUndef %float |
| %24 = OpExtInst %ModfStructType %1 ModfStruct %23 |
| %25 = OpCompositeExtract %float %24 1 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, DontChangeInputStructs) { |
| // The input for a shader has to match the type of the output from the |
| // previous shader in the pipeline. Because of that, we cannot change the |
| // types of input variables. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %input_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 600 |
| %float = OpTypeFloat 32 |
| %void = OpTypeVoid |
| %21 = OpTypeFunction %void |
| %in_var_type = OpTypeStruct %float %float |
| %in_ptr_type = OpTypePointer Input %in_var_type |
| %input_var = OpVariable %in_ptr_type Input |
| %main = OpFunction %void None %21 |
| %22 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, DontChangeOutputStructs) { |
| // The output for a shader has to match the type of the output from the |
| // previous shader in the pipeline. Because of that, we cannot change the |
| // types of output variables. |
| const std::string text = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main" %output_var |
| OpExecutionMode %main OriginUpperLeft |
| OpSource HLSL 600 |
| %float = OpTypeFloat 32 |
| %void = OpTypeVoid |
| %21 = OpTypeFunction %void |
| %out_var_type = OpTypeStruct %float %float |
| %out_ptr_type = OpTypePointer Output %out_var_type |
| %output_var = OpVariable %out_ptr_type Output |
| %main = OpFunction %void None %21 |
| %22 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpExtract) { |
| // Test that an extract in an OpSpecConstantOp is correctly updated. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1 |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 |
| ; CHECK: %type__Globals = OpTypeStruct %uint |
| ; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]] |
| ; CHECK: OpSpecConstantOp %uint CompositeExtract [[struct]] 0 |
| OpCapability Shader |
| OpCapability Addresses |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %main "main" |
| OpDecorate %c_0 SpecId 0 |
| OpDecorate %c_1 SpecId 1 |
| OpDecorate %c_2 SpecId 2 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| %uint = OpTypeInt 32 0 |
| %c_0 = OpSpecConstant %uint 0 |
| %c_1 = OpSpecConstant %uint 1 |
| %c_2 = OpSpecConstant %uint 2 |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_2 = OpConstant %uint 2 |
| %uint_3 = OpConstant %uint 3 |
| %type__Globals = OpTypeStruct %uint %uint %uint |
| %spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2 |
| %extract = OpSpecConstantOp %uint CompositeExtract %spec_const_global 1 |
| %void = OpTypeVoid |
| %14 = OpTypeFunction %void |
| %main = OpFunction %void None %14 |
| %16 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpInsert) { |
| // Test that an insert in an OpSpecConstantOp is correctly updated. |
| const std::string text = R"( |
| ; CHECK: OpName |
| ; CHECK-NEXT: OpMemberName %type__Globals 0 "y" |
| ; CHECK-NOT: OpMemberName |
| ; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1 |
| ; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 |
| ; CHECK: %type__Globals = OpTypeStruct %uint |
| ; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]] |
| ; CHECK: OpSpecConstantOp %type__Globals CompositeInsert %uint_3 [[struct]] 0 |
| OpCapability Shader |
| OpCapability Addresses |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %main "main" |
| OpSource HLSL 600 |
| OpName %type__Globals "type.$Globals" |
| OpMemberName %type__Globals 0 "x" |
| OpMemberName %type__Globals 1 "y" |
| OpMemberName %type__Globals 2 "z" |
| OpName %main "main" |
| OpDecorate %c_0 SpecId 0 |
| OpDecorate %c_1 SpecId 1 |
| OpDecorate %c_2 SpecId 2 |
| OpMemberDecorate %type__Globals 0 Offset 0 |
| OpMemberDecorate %type__Globals 1 Offset 4 |
| OpMemberDecorate %type__Globals 2 Offset 16 |
| %uint = OpTypeInt 32 0 |
| %c_0 = OpSpecConstant %uint 0 |
| %c_1 = OpSpecConstant %uint 1 |
| %c_2 = OpSpecConstant %uint 2 |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_2 = OpConstant %uint 2 |
| %uint_3 = OpConstant %uint 3 |
| %type__Globals = OpTypeStruct %uint %uint %uint |
| %spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2 |
| %insert = OpSpecConstantOp %type__Globals CompositeInsert %uint_3 %spec_const_global 1 |
| %extract = OpSpecConstantOp %uint CompositeExtract %insert 1 |
| %void = OpTypeVoid |
| %14 = OpTypeFunction %void |
| %main = OpFunction %void None %14 |
| %16 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, 8BitIndexNoChange) { |
| // Test that the pass does not crash when an 8 bit index is used in an |
| // OpAccessChain. No change is expected. |
| const std::string text = R"( |
| OpCapability ImageQuery |
| OpCapability Int8 |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "OpnSeman/" %2 |
| OpExecutionMode %1 OriginUpperLeft |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %_struct_7 = OpTypeStruct %v4float |
| %_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %10 = OpTypeFunction %v4float %_ptr_Function__struct_7 |
| %char = OpTypeInt 8 1 |
| %char_0 = OpConstant %char 0 |
| %_ptr_Function_v4float = OpTypePointer Function %v4float |
| %2 = OpVariable %_ptr_Output_v4float Output |
| %1 = OpFunction %void None %4 |
| %14 = OpLabel |
| %15 = OpVariable %_ptr_Function__struct_7 Function |
| %16 = OpFunctionCall %v4float %17 %15 |
| OpReturn |
| OpFunctionEnd |
| %17 = OpFunction %v4float DontInline %10 |
| %18 = OpFunctionParameter %_ptr_Function__struct_7 |
| %19 = OpLabel |
| %20 = OpAccessChain %_ptr_Function_v4float %18 %char_0 |
| %21 = OpLoad %v4float %20 |
| OpReturnValue %21 |
| OpFunctionEnd |
| )"; |
| |
| auto result = SinglePassRunAndDisassemble<opt::EliminateDeadMembersPass>( |
| text, /* skip_nop = */ true, /* do_validation = */ true); |
| EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); |
| } |
| |
| TEST_F(EliminateDeadMemberTest, 8BitIndexWithChange) { |
| // Test that the pass does not crash when an 8 bit index is used in an |
| // OpAccessChain. The index in the access change should be changed to 0. |
| const std::string text = R"( |
| OpCapability ImageQuery |
| OpCapability Int8 |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %1 "OpnSeman/" %2 |
| OpExecutionMode %1 OriginUpperLeft |
| %void = OpTypeVoid |
| %4 = OpTypeFunction %void |
| %float = OpTypeFloat 32 |
| %v4float = OpTypeVector %float 4 |
| %_struct_7 = OpTypeStruct %v4float %v4float |
| %_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 |
| %_ptr_Output_v4float = OpTypePointer Output %v4float |
| %10 = OpTypeFunction %v4float %_ptr_Function__struct_7 |
| %char = OpTypeInt 8 1 |
| %char_1 = OpConstant %char 1 |
| %_ptr_Function_v4float = OpTypePointer Function %v4float |
| %2 = OpVariable %_ptr_Output_v4float Output |
| %1 = OpFunction %void None %4 |
| %14 = OpLabel |
| %15 = OpVariable %_ptr_Function__struct_7 Function |
| %16 = OpFunctionCall %v4float %17 %15 |
| OpReturn |
| OpFunctionEnd |
| %17 = OpFunction %v4float DontInline %10 |
| ; CHECK: [[param:%\w+]] = OpFunctionParameter |
| %18 = OpFunctionParameter %_ptr_Function__struct_7 |
| %19 = OpLabel |
| ; CHECK: OpAccessChain %_ptr_Function_v4float [[param]] %uint_0 |
| %20 = OpAccessChain %_ptr_Function_v4float %18 %char_1 |
| %21 = OpLoad %v4float %20 |
| OpReturnValue %21 |
| OpFunctionEnd |
| )"; |
| |
| SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true); |
| } |
| |
| } // namespace |