Split EliminateDeadInputComponents into safe and unsafe versions. (#4984)

Safe version will only optimize vertex shaders. All other shaders will
succeed without change.

Change --eliminate-dead-input-components to use new safe version.

Unsafe version (allowing non-vertex shaders) currently only available
through API. Should only be used in combination with other optimizations
to keep interfaces consistent. See optimizer.hpp for more details.
This commit is contained in:
Greg Fischer 2022-11-14 11:44:26 -07:00 коммит произвёл GitHub
Родитель a8647f59c0
Коммит 8ea3ae6be2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 128 добавлений и 40 удалений

Просмотреть файл

@ -913,6 +913,14 @@ Optimizer::PassToken CreateEliminateDeadInputComponentsPass();
// shader, then apply EliminateDeadOutputStores to this shader.
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass();
// Removes unused components from composite input variables. This safe
// version will not cause interface incompatibilities since it only changes
// vertex shaders. The current implementation just removes trailing unused
// components from input structs and 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 CreateEliminateDeadInputComponentsSafePass();
// Analyzes shader and populates |live_locs| and |live_builtins|. Best results
// will be obtained if shader has all dead code eliminated first. |live_locs|
// and |live_builtins| are subsequently used when calling

Просмотреть файл

@ -35,7 +35,11 @@ namespace spvtools {
namespace opt {
Pass::Status EliminateDeadInputComponentsPass::Process() {
// Current functionality assumes shader capability
// Process non-vertex only if explicitly allowed.
auto stage = context()->GetStage();
if (stage != spv::ExecutionModel::Vertex && vertex_shader_only_)
return Status::SuccessWithoutChange;
// Current functionality assumes shader capability.
if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
return Status::SuccessWithoutChange;
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
@ -62,13 +66,21 @@ Pass::Status EliminateDeadInputComponentsPass::Process() {
}
const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray();
if (arr_type != nullptr) {
// Only process array if input of vertex shader, or output of
// fragment shader. Otherwise, if one shader has a runtime index and the
// other does not, interface incompatibility can occur.
if (!((ptr_type->storage_class() == spv::StorageClass::Input &&
stage == spv::ExecutionModel::Vertex) ||
(ptr_type->storage_class() == spv::StorageClass::Output &&
stage == spv::ExecutionModel::Fragment)))
continue;
unsigned arr_len_id = arr_type->LengthId();
Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id);
if (arr_len_inst->opcode() != spv::Op::OpConstant) {
continue;
}
// SPIR-V requires array size is >= 1, so this works for signed or
// unsigned size
// unsigned size.
unsigned original_max =
arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1;
unsigned max_idx = FindMaxIndex(var, original_max);
@ -92,7 +104,7 @@ Pass::Status EliminateDeadInputComponentsPass::Process() {
}
// Move changed vars after their new type instruction to preserve backward
// referencing
// referencing.
for (auto var : vars_to_move) {
auto type_id = var->type_id();
auto type_inst = def_use_mgr->GetDef(type_id);

Просмотреть файл

@ -28,13 +28,14 @@ namespace opt {
// See optimizer.hpp for documentation.
class EliminateDeadInputComponentsPass : public Pass {
public:
explicit EliminateDeadInputComponentsPass(bool output_instead = false)
: output_instead_(output_instead) {}
explicit EliminateDeadInputComponentsPass(bool output_instead = false,
bool vertex_shader_only = true)
: output_instead_(output_instead),
vertex_shader_only_(vertex_shader_only) {}
const char* name() const override {
return "eliminate-dead-input-components";
}
Status Process() override;
// Return the mask of preserved Analyses.
@ -61,6 +62,9 @@ class EliminateDeadInputComponentsPass : public Pass {
// Process output variables instead
bool output_instead_;
// Only process vertex shaders
bool vertex_shader_only_;
};
} // namespace opt

Просмотреть файл

@ -26,7 +26,6 @@ static const int kInstCommonParamInstIdx = 0;
static const int kInstCommonParamCnt = 1;
// Indices of operands in SPIR-V instructions
static const int kEntryPointExecutionModelInIdx = 0;
static const int kEntryPointFunctionIdInIdx = 1;
} // anonymous namespace
@ -1056,22 +1055,7 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
// one model per module. In such cases we will need
// to clone any functions which are in the call trees of entrypoints
// with differing execution models.
uint32_t ecnt = 0;
auto stage = spv::ExecutionModel::Max;
for (auto& e : get_module()->entry_points()) {
if (ecnt == 0)
stage = spv::ExecutionModel(
e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx));
else if (spv::ExecutionModel(e.GetSingleWordInOperand(
kEntryPointExecutionModelInIdx)) != stage) {
if (consumer()) {
std::string message = "Mixed stage shader module not supported";
consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
}
return false;
}
++ecnt;
}
spv::ExecutionModel stage = context()->GetStage();
// Check for supported stages
if (stage != spv::ExecutionModel::Vertex &&
stage != spv::ExecutionModel::Fragment &&

Просмотреть файл

@ -525,7 +525,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
} else if (pass_name == "remove-dont-inline") {
RegisterPass(CreateRemoveDontInlinePass());
} else if (pass_name == "eliminate-dead-input-components") {
RegisterPass(CreateEliminateDeadInputComponentsPass());
RegisterPass(CreateEliminateDeadInputComponentsSafePass());
} else if (pass_name == "fix-func-call-param") {
RegisterPass(CreateFixFuncCallArgumentsPass());
} else if (pass_name == "convert-to-sampled-image") {
@ -1016,6 +1016,18 @@ Optimizer::PassToken CreateInterpolateFixupPass() {
}
Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::EliminateDeadInputComponentsPass>(
/* output_instead */ false, /* vertex_shader_only */ false));
}
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::EliminateDeadInputComponentsPass>(
/* output_instead */ true, /* vertex_shader_only */ false));
}
Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::EliminateDeadInputComponentsPass>());
}
@ -1034,12 +1046,6 @@ Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
}
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::EliminateDeadInputComponentsPass>(
/* output_instead */ true));
}
Optimizer::PassToken CreateConvertToSampledImagePass(
const std::vector<opt::DescriptorSetAndBinding>&
descriptor_set_binding_pairs) {

Просмотреть файл

@ -85,7 +85,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
false);
}
TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
@ -135,7 +136,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
false);
}
TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
@ -202,7 +204,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
false);
}
TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
@ -268,7 +271,8 @@ TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
false);
}
TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
@ -350,7 +354,8 @@ TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
false);
}
TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
@ -396,7 +401,8 @@ TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
false);
}
TEST_F(ElimDeadInputComponentsTest, ElimStructMember) {
@ -460,7 +466,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimStructMember) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
false);
}
TEST_F(ElimDeadInputComponentsTest, ElimOutputStructMember) {
@ -558,7 +565,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOutputStructMember) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true,
false);
}
TEST_F(ElimDeadInputComponentsTest, ElimOutputArrayMembers) {
@ -577,7 +585,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOutputArrayMembers) {
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %uv
OpEntryPoint Fragment %main "main" %uv
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %uv "uv"
@ -609,7 +618,72 @@ TEST_F(ElimDeadInputComponentsTest, ElimOutputArrayMembers) {
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true,
false);
}
TEST_F(ElimDeadInputComponentsTest, VertexOnly) {
// Should NOT eliminate uv
//
// #version 450
//
// in Vertex {
// vec4 Cd;
// vec2 uv;
// } iVert;
//
// out vec4 fragColor;
//
// void main()
// {
// vec4 color = vec4(iVert.Cd);
// fragColor = color;
// }
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %iVert %fragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %Vertex "Vertex"
OpMemberName %Vertex 0 "Cd"
OpMemberName %Vertex 1 "uv"
OpName %iVert "iVert"
OpName %fragColor "fragColor"
OpDecorate %Vertex Block
OpDecorate %iVert Location 0
OpDecorate %fragColor Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
%Vertex = OpTypeStruct %v4float %v2float
; CHECK: %Vertex = OpTypeStruct %v4float %v2float
%_ptr_Input_Vertex = OpTypePointer Input %Vertex
; CHECK: %_ptr_Input_Vertex = OpTypePointer Input %Vertex
%iVert = OpVariable %_ptr_Input_Vertex Input
; CHECK: %iVert = OpVariable %_ptr_Input_Vertex Input
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
%fragColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %3
%5 = OpLabel
%17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
%18 = OpLoad %v4float %17
OpStore %fragColor %18
OpReturn
OpFunctionEnd
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
true);
}
} // namespace