spirv-opt: (WIP) Eliminate Dead Input Component Pass (#4720)

This adds the --eliminate-dead-input-components pass which currently
removes trailing unused components from input arrays.

Fixes #4532
This commit is contained in:
Greg Fischer 2022-03-22 20:50:52 -06:00 committed by GitHub
parent f8cd51431e
commit 9d1b572884
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 648 additions and 14 deletions

View File

@ -106,6 +106,7 @@ SPVTOOLS_OPT_SRC_FILES := \
source/opt/eliminate_dead_constant_pass.cpp \
source/opt/eliminate_dead_functions_pass.cpp \
source/opt/eliminate_dead_functions_util.cpp \
source/opt/eliminate_dead_input_components_pass.cpp \
source/opt/eliminate_dead_members_pass.cpp \
source/opt/feature_manager.cpp \
source/opt/fix_storage_class.cpp \

View File

@ -611,6 +611,8 @@ static_library("spvtools_opt") {
"source/opt/eliminate_dead_functions_pass.h",
"source/opt/eliminate_dead_functions_util.cpp",
"source/opt/eliminate_dead_functions_util.h",
"source/opt/eliminate_dead_input_components_pass.cpp",
"source/opt/eliminate_dead_input_components_pass.h",
"source/opt/eliminate_dead_members_pass.cpp",
"source/opt/eliminate_dead_members_pass.h",
"source/opt/empty_pass.h",

View File

@ -886,6 +886,13 @@ Optimizer::PassToken CreateAmdExtToKhrPass();
// propagated into their final positions.
Optimizer::PassToken CreateInterpolateFixupPass();
// Removes unused components from composite input variables. Current
// implementation just removes trailing unused components from input arrays.
// The pass performs best after maximizing dead code removal. A subsequent dead
// code elimination pass would be beneficial in removing newly unused component
// types.
Optimizer::PassToken CreateEliminateDeadInputComponentsPass();
// Creates a convert-to-sampled-image pass to convert images and/or
// samplers with given pairs of descriptor set and binding to sampled image.
// If a pair of an image and a sampler have the same pair of descriptor set and

View File

@ -45,6 +45,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
eliminate_dead_constant_pass.h
eliminate_dead_functions_pass.h
eliminate_dead_functions_util.h
eliminate_dead_input_components_pass.h
eliminate_dead_members_pass.h
empty_pass.h
feature_manager.h
@ -158,6 +159,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
eliminate_dead_constant_pass.cpp
eliminate_dead_functions_pass.cpp
eliminate_dead_functions_util.cpp
eliminate_dead_input_components_pass.cpp
eliminate_dead_members_pass.cpp
feature_manager.cpp
fix_storage_class.cpp

View File

@ -0,0 +1,146 @@
// Copyright (c) 2022 The Khronos Group Inc.
// Copyright (c) 2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/opt/eliminate_dead_input_components_pass.h"
#include <set>
#include <vector>
#include "source/opt/instruction.h"
#include "source/opt/ir_builder.h"
#include "source/opt/ir_context.h"
#include "source/util/bit_vector.h"
namespace {
const uint32_t kAccessChainBaseInIdx = 0;
const uint32_t kAccessChainIndex0InIdx = 1;
const uint32_t kConstantValueInIdx = 0;
const uint32_t kVariableStorageClassInIdx = 0;
} // namespace
namespace spvtools {
namespace opt {
Pass::Status EliminateDeadInputComponentsPass::Process() {
// Current functionality assumes shader capability
if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
return Status::SuccessWithoutChange;
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
bool modified = false;
std::vector<std::pair<Instruction*, unsigned>> arrays_to_change;
for (auto& var : context()->types_values()) {
if (var.opcode() != SpvOpVariable) {
continue;
}
analysis::Type* var_type = type_mgr->GetType(var.type_id());
analysis::Pointer* ptr_type = var_type->AsPointer();
if (ptr_type == nullptr) {
continue;
}
if (ptr_type->storage_class() != SpvStorageClassInput) {
continue;
}
const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray();
if (arr_type == nullptr) {
continue;
}
unsigned arr_len_id = arr_type->LengthId();
Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id);
if (arr_len_inst->opcode() != SpvOpConstant) {
continue;
}
// SPIR-V requires array size is >= 1, so this works for signed or
// unsigned size
unsigned original_max =
arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1;
unsigned max_idx = FindMaxIndex(var, original_max);
if (max_idx != original_max) {
ChangeArrayLength(var, max_idx + 1);
modified = true;
}
}
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
unsigned EliminateDeadInputComponentsPass::FindMaxIndex(Instruction& var,
unsigned original_max) {
unsigned max = 0;
bool seen_non_const_ac = false;
assert(var.opcode() == SpvOpVariable && "must be variable");
context()->get_def_use_mgr()->WhileEachUser(
var.result_id(), [&max, &seen_non_const_ac, var, this](Instruction* use) {
auto use_opcode = use->opcode();
if (use_opcode == SpvOpLoad || use_opcode == SpvOpCopyMemory ||
use_opcode == SpvOpCopyMemorySized ||
use_opcode == SpvOpCopyObject) {
seen_non_const_ac = true;
return false;
}
if (use->opcode() != SpvOpAccessChain &&
use->opcode() != SpvOpInBoundsAccessChain) {
return true;
}
// OpAccessChain with no indices currently not optimized
if (use->NumInOperands() == 1) {
seen_non_const_ac = true;
return false;
}
unsigned base_id = use->GetSingleWordInOperand(kAccessChainBaseInIdx);
USE_ASSERT(base_id == var.result_id() && "unexpected base");
unsigned idx_id = use->GetSingleWordInOperand(kAccessChainIndex0InIdx);
Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id);
if (idx_inst->opcode() != SpvOpConstant) {
seen_non_const_ac = true;
return false;
}
unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx);
if (value > max) max = value;
return true;
});
return seen_non_const_ac ? original_max : max;
}
void EliminateDeadInputComponentsPass::ChangeArrayLength(Instruction& arr,
unsigned length) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
analysis::Pointer* ptr_type = type_mgr->GetType(arr.type_id())->AsPointer();
const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray();
assert(arr_ty && "expecting array type");
uint32_t length_id = const_mgr->GetUIntConst(length);
analysis::Array new_arr_ty(arr_ty->element_type(),
arr_ty->GetConstantLengthInfo(length_id, length));
analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty);
analysis::Pointer new_ptr_ty(reg_new_arr_ty, SpvStorageClassInput);
analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
arr.SetResultType(new_ptr_ty_id);
def_use_mgr->AnalyzeInstUse(&arr);
// Move array OpVariable instruction after its new type to preserve order
USE_ASSERT(arr.GetSingleWordInOperand(kVariableStorageClassInIdx) !=
SpvStorageClassFunction &&
"cannot move Function variable");
Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id);
arr.RemoveFromList();
arr.InsertAfter(new_ptr_ty_inst);
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,59 @@
// Copyright (c) 2022 The Khronos Group Inc.
// Copyright (c) 2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
#define SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
#include <unordered_map>
#include "source/opt/ir_context.h"
#include "source/opt/module.h"
#include "source/opt/pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
class EliminateDeadInputComponentsPass : public Pass {
public:
explicit EliminateDeadInputComponentsPass() {}
const char* name() const override { return "reduce-load-size"; }
Status Process() override;
// Return the mask of preserved Analyses.
IRContext::Analysis GetPreservedAnalyses() override {
return IRContext::kAnalysisDefUse |
IRContext::kAnalysisInstrToBlockMapping |
IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG |
IRContext::kAnalysisDominatorAnalysis |
IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
}
private:
// Find the max constant used to index the variable declared by |var|
// through OpAccessChain or OpInBoundsAccessChain. If any non-constant
// indices or non-Op*AccessChain use of |var|, return |original_max|.
unsigned FindMaxIndex(Instruction& var, unsigned original_max);
// Change the length of the array |inst| to |length|
void ChangeArrayLength(Instruction& inst, unsigned length);
};
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_

View File

@ -39,13 +39,6 @@ static const int kSpvTypeImageMS = 4;
static const int kSpvTypeImageSampled = 5;
} // anonymous namespace
// Avoid unused variable warning/error on Linux
#ifndef NDEBUG
#define USE_ASSERT(x) assert(x)
#else
#define USE_ASSERT(x) ((void)(x))
#endif
namespace spvtools {
namespace opt {

View File

@ -31,13 +31,6 @@ namespace {
// Input Operand Indices
static const int kSpvVariableStorageClassInIdx = 0;
// Avoid unused variable warning/error on Linux
#ifndef NDEBUG
#define USE_ASSERT(x) assert(x)
#else
#define USE_ASSERT(x) ((void)(x))
#endif
// Folding rule function which attempts to replace |op(OpLoad(a),...)|
// by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt*
// instructions. Returns true if replaced, false otherwise.

View File

@ -523,6 +523,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
RegisterPass(CreateInterpolateFixupPass());
} else if (pass_name == "remove-dont-inline") {
RegisterPass(CreateRemoveDontInlinePass());
} else if (pass_name == "eliminate-dead-input-components") {
RegisterPass(CreateEliminateDeadInputComponentsPass());
} else if (pass_name == "convert-to-sampled-image") {
if (pass_args.size() > 0) {
auto descriptor_set_binding_pairs =
@ -1004,6 +1006,11 @@ Optimizer::PassToken CreateInterpolateFixupPass() {
MakeUnique<opt::InterpFixupPass>());
}
Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::EliminateDeadInputComponentsPass>());
}
Optimizer::PassToken CreateConvertToSampledImagePass(
const std::vector<opt::DescriptorSetAndBinding>&
descriptor_set_binding_pairs) {

View File

@ -28,6 +28,13 @@
#include "spirv-tools/libspirv.hpp"
#include "types.h"
// Avoid unused variable warning/error on Linux
#ifndef NDEBUG
#define USE_ASSERT(x) assert(x)
#else
#define USE_ASSERT(x) ((void)(x))
#endif
namespace spvtools {
namespace opt {

View File

@ -34,6 +34,7 @@
#include "source/opt/desc_sroa.h"
#include "source/opt/eliminate_dead_constant_pass.h"
#include "source/opt/eliminate_dead_functions_pass.h"
#include "source/opt/eliminate_dead_input_components_pass.h"
#include "source/opt/eliminate_dead_members_pass.h"
#include "source/opt/empty_pass.h"
#include "source/opt/fix_storage_class.h"

View File

@ -419,6 +419,12 @@ size_t Array::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
void Array::ReplaceElementType(const Type* type) { element_type_ = type; }
Array::LengthInfo Array::GetConstantLengthInfo(uint32_t const_id,
uint32_t length) const {
std::vector<uint32_t> extra_words{LengthInfo::Case::kConstant, length};
return {const_id, extra_words};
}
RuntimeArray::RuntimeArray(const Type* type)
: Type(kRuntimeArray), element_type_(type) {
assert(!type->AsVoid());

View File

@ -390,6 +390,7 @@ class Array : public Type {
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
void ReplaceElementType(const Type* element_type);
LengthInfo GetConstantLengthInfo(uint32_t const_id, uint32_t length) const;
private:
bool IsSameImpl(const Type* that, IsSameCache*) const override;

View File

@ -42,6 +42,7 @@ add_spvtools_unittest(TARGET opt
desc_sroa_test.cpp
eliminate_dead_const_test.cpp
eliminate_dead_functions_test.cpp
eliminate_dead_input_components_test.cpp
eliminate_dead_member_test.cpp
feature_manager_test.cpp
fix_storage_class_test.cpp

View File

@ -0,0 +1,404 @@
// Copyright (c) 2022 The Khronos Group Inc.
// Copyright (c) 2022 LunarG Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include "gmock/gmock.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using ElimDeadInputComponentsTest = PassTest<::testing::Test>;
TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) {
// Should reduce to uv[2]
//
// #version 450
//
// layout(location = 0) in vec4 uv[8];
//
// out gl_PerVertex {
// vec4 gl_Position;
// };
//
// void main()
// {
// gl_Position = uv[1];
// }
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %uv
OpSource GLSL 450
OpName %main "main"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpName %uv "uv"
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
OpDecorate %uv Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_8 = OpConstant %uint 8
%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
%uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%int_1 = OpConstant %int 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
%21 = OpLoad %v4float %20
%23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %23 %21
OpReturn
OpFunctionEnd
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
}
TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
// Same as ElimOneConstantIndex but with OpInBoundsAccessChain
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %uv
OpSource GLSL 450
OpName %main "main"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpName %uv "uv"
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
OpDecorate %uv Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_8 = OpConstant %uint 8
%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
%uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%int_1 = OpConstant %int 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpInBoundsAccessChain %_ptr_Input_v4float %uv %int_1
%21 = OpLoad %v4float %20
%23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %23 %21
OpReturn
OpFunctionEnd
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
}
TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
// Should reduce to uv[4]
//
// #version 450
//
// layout(location = 0) in vec4 uv[8];
//
// out gl_PerVertex {
// vec4 gl_Position;
// };
//
// void main()
// {
// gl_Position = uv[1] + uv[3];
// }
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %uv
OpSource GLSL 450
OpName %main "main"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpName %uv "uv"
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
OpDecorate %uv Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_8 = OpConstant %uint 8
%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
%uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%int_1 = OpConstant %int 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%int_3 = OpConstant %int 3
%_ptr_Output_v4float = OpTypePointer Output %v4float
;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_4 Input
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
%21 = OpLoad %v4float %20
%23 = OpAccessChain %_ptr_Input_v4float %uv %int_3
%24 = OpLoad %v4float %23
%25 = OpFAdd %v4float %21 %24
%27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %27 %25
OpReturn
OpFunctionEnd
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
}
TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
// Should not reduce uv[8] because of max index of 7
//
// #version 450
//
// layout(location = 0) in vec4 uv[8];
//
// out gl_PerVertex {
// vec4 gl_Position;
// };
//
// void main()
// {
// gl_Position = uv[1] + uv[7];
// }
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %uv
OpSource GLSL 450
OpName %main "main"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpName %uv "uv"
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
OpDecorate %uv Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_8 = OpConstant %uint 8
%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
%uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%int_1 = OpConstant %int 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%int_7 = OpConstant %int 7
%_ptr_Output_v4float = OpTypePointer Output %v4float
;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
%21 = OpLoad %v4float %20
%23 = OpAccessChain %_ptr_Input_v4float %uv %int_7
%24 = OpLoad %v4float %23
%25 = OpFAdd %v4float %21 %24
%27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %27 %25
OpReturn
OpFunctionEnd
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
}
TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
// Should not reduce uv[8] because of non-constant index of ui
//
// #version 450
//
// layout(location = 0) in vec4 uv[8];
//
// out gl_PerVertex {
// vec4 gl_Position;
// };
//
// uniform ubname {
// int ui;
// } ubinst;
//
// void main()
// {
// gl_Position = uv[1] + uv[ubinst.ui];
// }
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %uv %ubinst
OpSource GLSL 450
OpName %main "main"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpName %uv "uv"
OpName %ubname "ubname"
OpMemberName %ubname 0 "ui"
OpName %ubinst "ubinst"
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
OpDecorate %uv Location 0
OpMemberDecorate %ubname 0 Offset 0
OpDecorate %ubname Block
OpDecorate %ubinst DescriptorSet 0
OpDecorate %ubinst Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_8 = OpConstant %uint 8
%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
%uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%int_1 = OpConstant %int 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%ubname = OpTypeStruct %int
%_ptr_Uniform_ubname = OpTypePointer Uniform %ubname
%ubinst = OpVariable %_ptr_Uniform_ubname Uniform
%_ptr_Uniform_int = OpTypePointer Uniform %int
%_ptr_Output_v4float = OpTypePointer Output %v4float
;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
%21 = OpLoad %v4float %20
%26 = OpAccessChain %_ptr_Uniform_int %ubinst %int_0
%27 = OpLoad %int %26
%28 = OpAccessChain %_ptr_Input_v4float %uv %27
%29 = OpLoad %v4float %28
%30 = OpFAdd %v4float %21 %29
%32 = OpAccessChain %_ptr_Output_v4float %_ %int_0
OpStore %32 %30
OpReturn
OpFunctionEnd
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
}
TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
// Should not change due to non-indexed access chain
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %_ %uv
OpSource GLSL 450
OpName %main "main"
OpName %gl_PerVertex "gl_PerVertex"
OpMemberName %gl_PerVertex 0 "gl_Position"
OpName %_ ""
OpName %uv "uv"
OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
OpDecorate %gl_PerVertex Block
OpDecorate %uv Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%gl_PerVertex = OpTypeStruct %v4float
%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
%_ = OpVariable %_ptr_Output_gl_PerVertex Output
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_8 = OpConstant %uint 8
%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
%uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%int_1 = OpConstant %int 1
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
%main = OpFunction %void None %3
%5 = OpLabel
%20 = OpAccessChain %_ptr_Input__arr_v4float_uint_8 %uv
OpReturn
OpFunctionEnd
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@ -208,6 +208,10 @@ Options (in lexicographical order):)",
unused stores to vector components, that are not removed by
aggressive dead code elimination.)");
printf(R"(
--eliminate-dead-input-components
Deletes unused components from input variables. Currently
deletes trailing unused elements from input arrays.)");
printf(R"(
--eliminate-dead-variables
Deletes module scope variables that are not referenced.)");
printf(R"(