mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-25 21:10:04 +00:00
24476c2e32
* Don't eliminate dead members from StructuredBuffer as layout(offset) qualifiers cannot be applied to structure fields. * Traverse arrays when marking structs as fully used. Co-authored-by: Steven Perron <stevenperron@google.com>
1330 lines
56 KiB
C++
1330 lines
56 KiB
C++
// 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
|
|
%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
|