mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 00:40:14 +00:00
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:
parent
f8cd51431e
commit
9d1b572884
@ -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 \
|
||||
|
2
BUILD.gn
2
BUILD.gn
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
146
source/opt/eliminate_dead_input_components_pass.cpp
Normal file
146
source/opt/eliminate_dead_input_components_pass.cpp
Normal 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
|
59
source/opt/eliminate_dead_input_components_pass.h
Normal file
59
source/opt/eliminate_dead_input_components_pass.h
Normal 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_
|
@ -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 {
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
404
test/opt/eliminate_dead_input_components_test.cpp
Normal file
404
test/opt/eliminate_dead_input_components_test.cpp
Normal 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
|
@ -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"(
|
||||
|
Loading…
Reference in New Issue
Block a user