Squashed 'third_party/SPIRV-Tools/' changes from 1a7f71afb..597631b69 597631b69 spirv-opt: Handle null CompositeInsert (#4998) 81ec2aaa0 Add option to ADCE to remove output variables from interface. (#4994) 46ca66e69 Add support for tesc, tese and geom to EliminateDead*Components (#4990) git-subtree-dir: third_party/SPIRV-Tools git-subtree-split: 597631b6934397c5f2ed221e04d6ebe451ba0361 Change-Id: Ibb74f6ea53f18869eeea215370232678e6276ef4
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 41752d6..aa6a614 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp
@@ -521,8 +521,12 @@ // interface are considered live and are not eliminated. This mode is needed // by GPU-Assisted validation instrumentation, where a change in the interface // is not allowed. -Optimizer::PassToken CreateAggressiveDCEPass(); -Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface); +// +// If |remove_outputs| is true, allow outputs to be removed from the interface. +// This is only safe if the caller knows that there is no corresponding input +// variable in the following shader. It is false by default. +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface = false, + bool remove_outputs = false); // Creates a remove-unused-interface-variables pass. // Removes variables referenced on the |OpEntryPoint| instruction that are not
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index f68032d..53d13f1 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -579,8 +579,10 @@ auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); auto storage_class = var->GetSingleWordInOperand(0u); // Vulkan support outputs without an associated input, but not inputs - // without an associated output. - if (spv::StorageClass(storage_class) == spv::StorageClass::Output) { + // without an associated output. Don't remove outputs unless explicitly + // allowed. + if (!remove_outputs_ && + spv::StorageClass(storage_class) == spv::StorageClass::Output) { AddToWorklist(var); } }
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h index 99c4739..fbe08ad 100644 --- a/source/opt/aggressive_dead_code_elim_pass.h +++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -44,8 +44,10 @@ using GetBlocksFunction = std::function<std::vector<BasicBlock*>*(const BasicBlock*)>; - AggressiveDCEPass(bool preserve_interface = false) - : preserve_interface_(preserve_interface) {} + AggressiveDCEPass(bool preserve_interface = false, + bool remove_outputs = false) + : preserve_interface_(preserve_interface), + remove_outputs_(remove_outputs) {} const char* name() const override { return "eliminate-dead-code-aggressive"; } Status Process() override; @@ -63,6 +65,11 @@ // is not allowed. bool preserve_interface_; + // Output variables can be removed from the interface if this is true. + // This is safe if the caller knows that the corresponding input variable + // in the following shader has been removed. It is false by default. + bool remove_outputs_; + // Return true if |varId| is a variable of |storageClass|. |varId| must either // be 0 or the result of an instruction. bool IsVarOfStorage(uint32_t varId, spv::StorageClass storageClass);
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index e91455e..19b39d6 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp
@@ -140,6 +140,12 @@ // Work down hierarchy and add all the indexes, not including the final // index. for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + if (composite->AsNullConstant()) { + // Return Null for the return type. + analysis::TypeManager* type_mgr = context->get_type_mgr(); + return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), {}); + } + if (i != inst->NumInOperands() - 1) { chain.push_back(composite); }
diff --git a/source/opt/eliminate_dead_input_components_pass.cpp b/source/opt/eliminate_dead_input_components_pass.cpp index 637a33b..fa019ac 100644 --- a/source/opt/eliminate_dead_input_components_pass.cpp +++ b/source/opt/eliminate_dead_input_components_pass.cpp
@@ -28,17 +28,26 @@ namespace { constexpr uint32_t kAccessChainBaseInIdx = 0; constexpr uint32_t kAccessChainIndex0InIdx = 1; +constexpr uint32_t kAccessChainIndex1InIdx = 2; constexpr uint32_t kConstantValueInIdx = 0; } // namespace Pass::Status EliminateDeadInputComponentsPass::Process() { // Process non-vertex only if explicitly allowed. - auto stage = context()->GetStage(); + const 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; + // Current functionality assumes vert, frag, tesc, tese or geom shader. + // TODO(issue #4988): Add GLCompute. + if (stage != spv::ExecutionModel::Vertex && + stage != spv::ExecutionModel::Fragment && + stage != spv::ExecutionModel::TessellationControl && + stage != spv::ExecutionModel::TessellationEvaluation && + stage != spv::ExecutionModel::Geometry) + return Status::SuccessWithoutChange; analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); analysis::TypeManager* type_mgr = context()->get_type_mgr(); bool modified = false; @@ -52,23 +61,39 @@ if (ptr_type == nullptr) { continue; } + const auto sclass = ptr_type->storage_class(); if (output_instead_) { - if (ptr_type->storage_class() != spv::StorageClass::Output) { + if (sclass != spv::StorageClass::Output) { continue; } } else { - if (ptr_type->storage_class() != spv::StorageClass::Input) { + if (sclass != spv::StorageClass::Input) { continue; } } - const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray(); + // For tesc, or input variables in tese or geom shaders, + // there is a outer per-vertex-array that must be ignored + // for the purposes of this analysis/optimization. Do the + // analysis on the inner type in these cases. + bool skip_first_index = false; + auto core_type = ptr_type->pointee_type(); + if (stage == spv::ExecutionModel::TessellationControl || + (sclass == spv::StorageClass::Input && + (stage == spv::ExecutionModel::TessellationEvaluation || + stage == spv::ExecutionModel::Geometry))) { + auto arr_type = core_type->AsArray(); + if (!arr_type) continue; + core_type = arr_type->element_type(); + skip_first_index = true; + } + const analysis::Array* arr_type = core_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 && + if (!((sclass == spv::StorageClass::Input && stage == spv::ExecutionModel::Vertex) || - (ptr_type->storage_class() == spv::StorageClass::Output && + (sclass == spv::StorageClass::Output && stage == spv::ExecutionModel::Fragment))) continue; unsigned arr_len_id = arr_type->LengthId(); @@ -88,13 +113,13 @@ } continue; } - const analysis::Struct* struct_type = ptr_type->pointee_type()->AsStruct(); + const analysis::Struct* struct_type = core_type->AsStruct(); if (struct_type == nullptr) continue; const auto elt_types = struct_type->element_types(); unsigned original_max = static_cast<unsigned>(elt_types.size()) - 1; - unsigned max_idx = FindMaxIndex(var, original_max); + unsigned max_idx = FindMaxIndex(var, original_max, skip_first_index); if (max_idx != original_max) { - ChangeStructLength(var, max_idx + 1); + ChangeIOVarStructLength(var, max_idx + 1); vars_to_move.push_back(&var); modified = true; } @@ -112,13 +137,15 @@ return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } -unsigned EliminateDeadInputComponentsPass::FindMaxIndex(Instruction& var, - unsigned original_max) { +unsigned EliminateDeadInputComponentsPass::FindMaxIndex( + const Instruction& var, const unsigned original_max, + const bool skip_first_index) { unsigned max = 0; bool seen_non_const_ac = false; assert(var.opcode() == spv::Op::OpVariable && "must be variable"); context()->get_def_use_mgr()->WhileEachUser( - var.result_id(), [&max, &seen_non_const_ac, var, this](Instruction* use) { + var.result_id(), [&max, &seen_non_const_ac, var, skip_first_index, + this](Instruction* use) { auto use_opcode = use->opcode(); if (use_opcode == spv::Op::OpLoad || use_opcode == spv::Op::OpStore || use_opcode == spv::Op::OpCopyMemory || @@ -132,13 +159,17 @@ return true; } // OpAccessChain with no indices currently not optimized - if (use->NumInOperands() == 1) { + if (use->NumInOperands() == 1 || + (skip_first_index && use->NumInOperands() == 2)) { seen_non_const_ac = true; return false; } - unsigned base_id = use->GetSingleWordInOperand(kAccessChainBaseInIdx); + const unsigned base_id = + use->GetSingleWordInOperand(kAccessChainBaseInIdx); USE_ASSERT(base_id == var.result_id() && "unexpected base"); - unsigned idx_id = use->GetSingleWordInOperand(kAccessChainIndex0InIdx); + const unsigned in_idx = skip_first_index ? kAccessChainIndex1InIdx + : kAccessChainIndex0InIdx; + const unsigned idx_id = use->GetSingleWordInOperand(in_idx); Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id); if (idx_inst->opcode() != spv::Op::OpConstant) { seen_non_const_ac = true; @@ -171,12 +202,17 @@ def_use_mgr->AnalyzeInstUse(&arr_var); } -void EliminateDeadInputComponentsPass::ChangeStructLength( - Instruction& struct_var, unsigned length) { +void EliminateDeadInputComponentsPass::ChangeIOVarStructLength( + Instruction& io_var, unsigned length) { analysis::TypeManager* type_mgr = context()->get_type_mgr(); analysis::Pointer* ptr_type = - type_mgr->GetType(struct_var.type_id())->AsPointer(); - const analysis::Struct* struct_ty = ptr_type->pointee_type()->AsStruct(); + type_mgr->GetType(io_var.type_id())->AsPointer(); + auto core_type = ptr_type->pointee_type(); + // Check for per-vertex-array of struct from tesc, tese and geom and grab + // embedded struct type. + const auto arr_type = core_type->AsArray(); + if (arr_type) core_type = arr_type->element_type(); + const analysis::Struct* struct_ty = core_type->AsStruct(); assert(struct_ty && "expecting struct type"); const auto orig_elt_types = struct_ty->element_types(); std::vector<const analysis::Type*> new_elt_types; @@ -194,14 +230,19 @@ } type_mgr->AttachDecoration(*dec, &new_struct_ty); } - analysis::Type* reg_new_struct_ty = - type_mgr->GetRegisteredType(&new_struct_ty); - analysis::Pointer new_ptr_ty(reg_new_struct_ty, ptr_type->storage_class()); + analysis::Type* reg_new_var_ty = type_mgr->GetRegisteredType(&new_struct_ty); + if (arr_type) { + analysis::Array new_arr_ty(reg_new_var_ty, arr_type->length_info()); + reg_new_var_ty = type_mgr->GetRegisteredType(&new_arr_ty); + } + auto sclass = + output_instead_ ? spv::StorageClass::Output : spv::StorageClass::Input; + analysis::Pointer new_ptr_ty(reg_new_var_ty, sclass); 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); - struct_var.SetResultType(new_ptr_ty_id); + io_var.SetResultType(new_ptr_ty_id); analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); - def_use_mgr->AnalyzeInstUse(&struct_var); + def_use_mgr->AnalyzeInstUse(&io_var); } } // namespace opt
diff --git a/source/opt/eliminate_dead_input_components_pass.h b/source/opt/eliminate_dead_input_components_pass.h index 111366e..bdfdc21 100644 --- a/source/opt/eliminate_dead_input_components_pass.h +++ b/source/opt/eliminate_dead_input_components_pass.h
@@ -52,13 +52,15 @@ // 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); + unsigned FindMaxIndex(const Instruction& var, const unsigned original_max, + const bool skip_first_index = false); // Change the length of the array |inst| to |length| void ChangeArrayLength(Instruction& inst, unsigned length); - // Change the length of the struct |struct_var| to |length| - void ChangeStructLength(Instruction& struct_var, unsigned length); + // Change the length of the struct in |io_var| to |length|. |io_var| + // is either the struct or a per-vertex-array of the struct. + void ChangeIOVarStructLength(Instruction& io_var, unsigned length); // Process output variables instead bool output_instead_;
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 4cf3292..8828c70 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp
@@ -785,14 +785,10 @@ MakeUnique<opt::SSARewritePass>()); } -Optimizer::PassToken CreateAggressiveDCEPass() { +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface, + bool remove_outputs) { return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::AggressiveDCEPass>(false)); -} - -Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) { - return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::AggressiveDCEPass>(preserve_interface)); + MakeUnique<opt::AggressiveDCEPass>(preserve_interface, remove_outputs)); } Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index e51098e..0d94151 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -7777,6 +7777,86 @@ SinglePassRunAndMatch<AggressiveDCEPass>(text, true); } +TEST_F(AggressiveDCETest, RemoveOutputTrue) { + // Remove dead n_out output variable from module + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %c_out %c_in %n_out +;CHECK: OpEntryPoint Vertex %main "main" %c_out %c_in + OpSource GLSL 450 + OpName %main "main" + OpName %c_out "c_out" + OpName %c_in "c_in" + OpName %n_out "n_out" + OpDecorate %c_out Location 0 + OpDecorate %c_in Location 0 + OpDecorate %n_out Location 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %c_out = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c_in = OpVariable %_ptr_Input_v4float Input + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %n_out = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v4float %c_in + OpStore %c_out %12 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<AggressiveDCEPass>(text, true, false, true); +} + +TEST_F(AggressiveDCETest, RemoveOutputFalse) { + // Remove dead n_out output variable from module + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %c_out %c_in %n_out +;CHECK: OpEntryPoint Vertex %main "main" %c_out %c_in %n_out + OpSource GLSL 450 + OpName %main "main" + OpName %c_out "c_out" + OpName %c_in "c_in" + OpName %n_out "n_out" + OpDecorate %c_out Location 0 + OpDecorate %c_in Location 0 + OpDecorate %n_out Location 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %c_out = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c_in = OpVariable %_ptr_Input_v4float Input + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %n_out = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v4float %c_in + OpStore %c_out %12 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<AggressiveDCEPass>(text, true, false, false); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/eliminate_dead_input_components_test.cpp b/test/opt/eliminate_dead_input_components_test.cpp index 667350d..48bda22 100644 --- a/test/opt/eliminate_dead_input_components_test.cpp +++ b/test/opt/eliminate_dead_input_components_test.cpp
@@ -686,6 +686,569 @@ true); } +TEST_F(ElimDeadInputComponentsTest, TescInput) { + // Eliminate PointSize, ClipDistance, CullDistance from gl_in[] + // + // #version 450 + // + // layout (vertices = 4) out; + // + // void main() + // { + // vec4 pos = gl_in[gl_InvocationID].gl_Position; + // gl_out[gl_InvocationID].gl_Position = pos; + // } + const std::string text = R"( + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %main "main" %gl_in %gl_InvocationID %gl_out + OpExecutionMode %main OutputVertices 4 + OpSource GLSL 450 + OpName %main "main" + OpName %pos "pos" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %gl_in "gl_in" + OpName %gl_InvocationID "gl_InvocationID" + OpName %gl_PerVertex_0 "gl_PerVertex" + OpMemberName %gl_PerVertex_0 0 "gl_Position" + OpName %gl_out "gl_out" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpDecorate %gl_InvocationID BuiltIn InvocationId + OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position + OpDecorate %gl_PerVertex_0 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 + %uint_32 = OpConstant %uint 32 +%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32 +%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_InvocationID = OpVariable %_ptr_Input_int Input + %int_0 = OpConstant %int 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_PerVertex_0 = OpTypeStruct %v4float + %uint_4 = OpConstant %uint 4 +%_arr_gl_PerVertex_0_uint_4 = OpTypeArray %gl_PerVertex_0 %uint_4 +%_ptr_Output__arr_gl_PerVertex_0_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_0_uint_4 + %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_0_uint_4 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK: %gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float +; CHECK: [[asty:%\w+]] = OpTypeArray [[sty]] %uint_32 +; CHECK: [[pasty:%\w+]] = OpTypePointer Input [[asty]] +; CHECK: %gl_in = OpVariable [[pasty]] Input + %main = OpFunction %void None %3 + %5 = OpLabel + %pos = OpVariable %_ptr_Function_v4float Function + %21 = OpLoad %int %gl_InvocationID + %24 = OpAccessChain %_ptr_Input_v4float %gl_in %21 %int_0 + %25 = OpLoad %v4float %24 + OpStore %pos %25 + %31 = OpLoad %int %gl_InvocationID + %32 = OpLoad %v4float %pos + %34 = OpAccessChain %_ptr_Output_v4float %gl_out %31 %int_0 + OpStore %34 %32 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false, + false); +} + +TEST_F(ElimDeadInputComponentsTest, TescOutput) { + // Eliminate PointSize, ClipDistance, CullDistance from gl_out[] + // + // #version 450 + // + // layout (vertices = 4) out; + // + // void main() + // { + // vec4 pos = gl_in[gl_InvocationID].gl_Position; + // gl_out[gl_InvocationID].gl_Position = pos; + // } + const std::string text = R"( + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %main "main" %gl_in %gl_InvocationID %gl_out + OpExecutionMode %main OutputVertices 4 + OpSource GLSL 450 + OpName %main "main" + OpName %pos "pos" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %gl_in "gl_in" + OpName %gl_InvocationID "gl_InvocationID" + OpName %gl_PerVertex_0 "gl_PerVertex" + OpMemberName %gl_PerVertex_0 0 "gl_Position" + OpMemberName %gl_PerVertex_0 1 "gl_PointSize" + OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex_0 3 "gl_CullDistance" + OpName %gl_out "gl_out" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %gl_InvocationID BuiltIn InvocationId + OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex_0 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float + %uint_32 = OpConstant %uint 32 +%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32 +%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_InvocationID = OpVariable %_ptr_Input_int Input + %int_0 = OpConstant %int 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 + %uint_4 = OpConstant %uint 4 +%_arr_gl_PerVertex_0_uint_4 = OpTypeArray %gl_PerVertex_0 %uint_4 +%_ptr_Output__arr_gl_PerVertex_0_uint_4 = OpTypePointer Output %_arr_gl_PerVertex_0_uint_4 + %gl_out = OpVariable %_ptr_Output__arr_gl_PerVertex_0_uint_4 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float +; CHECK: [[asty:%\w+]] = OpTypeArray [[sty]] %uint_4 +; CHECK: [[pasty:%\w+]] = OpTypePointer Output [[asty]] +; CHECK: %gl_out = OpVariable [[pasty]] Output + %main = OpFunction %void None %3 + %5 = OpLabel + %pos = OpVariable %_ptr_Function_v4float Function + %21 = OpLoad %int %gl_InvocationID + %24 = OpAccessChain %_ptr_Input_v4float %gl_in %21 %int_0 + %25 = OpLoad %v4float %24 + OpStore %pos %25 + %31 = OpLoad %int %gl_InvocationID + %32 = OpLoad %v4float %pos + %34 = OpAccessChain %_ptr_Output_v4float %gl_out %31 %int_0 + OpStore %34 %32 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true, + false); +} + +TEST_F(ElimDeadInputComponentsTest, TeseInput) { + // Eliminate PointSize, ClipDistance, CullDistance from gl_in[] + // + // #version 450 + // + // layout(triangles, ccw) in; + // layout(fractional_odd_spacing) in; + // layout(point_mode) in; + // + // void main() + // { + // vec4 p = gl_in[1].gl_Position; + // gl_Position = p; + // } + const std::string text = R"( + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %main "main" %gl_in %_ + OpExecutionMode %main Triangles + OpExecutionMode %main SpacingFractionalOdd + OpExecutionMode %main VertexOrderCcw + OpExecutionMode %main PointMode + OpSource GLSL 450 + OpName %main "main" + OpName %p "p" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %gl_in "gl_in" + OpName %gl_PerVertex_0 "gl_PerVertex" + OpMemberName %gl_PerVertex_0 0 "gl_Position" + OpName %_ "" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position + OpDecorate %gl_PerVertex_0 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 + %uint_32 = OpConstant %uint 32 +%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32 +%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_PerVertex_0 = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0 + %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK: %gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float +; CHECK: [[asty:%\w+]] = OpTypeArray [[sty]] %uint_32 +; CHECK: [[pasty:%\w+]] = OpTypePointer Input [[asty]] +; CHECK: %gl_in = OpVariable [[pasty]] Input + %main = OpFunction %void None %3 + %5 = OpLabel + %p = OpVariable %_ptr_Function_v4float Function + %22 = OpAccessChain %_ptr_Input_v4float %gl_in %int_1 %int_0 + %23 = OpLoad %v4float %22 + OpStore %p %23 + %27 = OpLoad %v4float %p + %29 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %29 %27 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false, + false); +} + +TEST_F(ElimDeadInputComponentsTest, TeseOutput) { + // Eliminate PointSize, ClipDistance, CullDistance from gl_out + // + // #version 450 + // + // layout(triangles, ccw) in; + // layout(fractional_odd_spacing) in; + // layout(point_mode) in; + // + // void main() + // { + // vec4 p = gl_in[1].gl_Position; + // gl_Position = p; + // } + const std::string text = R"( + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %main "main" %gl_in %_ + OpExecutionMode %main Triangles + OpExecutionMode %main SpacingFractionalOdd + OpExecutionMode %main VertexOrderCcw + OpExecutionMode %main PointMode + OpSource GLSL 450 + OpName %main "main" + OpName %p "p" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %gl_in "gl_in" + OpName %gl_PerVertex_0 "gl_PerVertex" + OpMemberName %gl_PerVertex_0 0 "gl_Position" + OpMemberName %gl_PerVertex_0 1 "gl_PointSize" + OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex_0 3 "gl_CullDistance" + OpName %_ "" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex_0 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float + %uint_32 = OpConstant %uint 32 +%_arr_gl_PerVertex_uint_32 = OpTypeArray %gl_PerVertex %uint_32 +%_ptr_Input__arr_gl_PerVertex_uint_32 = OpTypePointer Input %_arr_gl_PerVertex_uint_32 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_32 Input + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0 + %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK: %_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +; CHECK: %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %main = OpFunction %void None %3 + %5 = OpLabel + %p = OpVariable %_ptr_Function_v4float Function + %22 = OpAccessChain %_ptr_Input_v4float %gl_in %int_1 %int_0 + %23 = OpLoad %v4float %22 + OpStore %p %23 + %27 = OpLoad %v4float %p + %29 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %29 %27 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true, + false); +} + +TEST_F(ElimDeadInputComponentsTest, GeomInput) { + // Eliminate PointSize, ClipDistance, CullDistance from gl_in[] + // + // #version 450 + // + // layout(triangle_strip, max_vertices = 3) out; + // layout(triangles) in; + // + // void main() + // { + // for (int i = 0; i < 3; i++) + // { + // gl_Position = gl_in[i].gl_Position; + // EmitVertex(); + // } + // EndPrimitive(); + // } + const std::string text = R"( + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %_ %gl_in + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 3 + OpSource GLSL 460 + OpName %main "main" + OpName %i "i" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %gl_PerVertex_0 "gl_PerVertex" + OpMemberName %gl_PerVertex_0 0 "gl_Position" + OpMemberName %gl_PerVertex_0 1 "gl_PointSize" + OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex_0 3 "gl_CullDistance" + OpName %gl_in "gl_in" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex_0 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex_0 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %bool = OpTypeBool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output +%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 + %uint_3 = OpConstant %uint 3 +%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3 +%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %int_1 = OpConstant %int 1 +; CHECK: [[asty:%\w+]] = OpTypeArray %gl_PerVertex %uint_3 +; CHECK: [[pasty:%\w+]] = OpTypePointer Input [[asty]] +; CHECK: %gl_in = OpVariable [[pasty]] Input + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %i %int_0 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %int %i + %18 = OpSLessThan %bool %15 %int_3 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %32 = OpLoad %int %i + %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0 + %35 = OpLoad %v4float %34 + %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %37 %35 + OpEmitVertex + OpBranch %13 + %13 = OpLabel + %38 = OpLoad %int %i + %40 = OpIAdd %int %38 %int_1 + OpStore %i %40 + OpBranch %10 + %12 = OpLabel + OpEndPrimitive + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false, + false); +} + +TEST_F(ElimDeadInputComponentsTest, GeomOutput) { + // Eliminate PointSize, ClipDistance, CullDistance from gl_out + // + // #version 450 + // + // layout(triangle_strip, max_vertices = 3) out; + // layout(triangles) in; + // + // void main() + // { + // for (int i = 0; i < 3; i++) + // { + // gl_Position = gl_in[i].gl_Position; + // EmitVertex(); + // } + // EndPrimitive(); + // } + const std::string text = R"( + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %_ %gl_in + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 3 + OpSource GLSL 460 + OpName %main "main" + OpName %i "i" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %gl_PerVertex_0 "gl_PerVertex" + OpMemberName %gl_PerVertex_0 0 "gl_Position" + OpName %gl_in "gl_in" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position + OpDecorate %gl_PerVertex_0 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %bool = OpTypeBool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output +%gl_PerVertex_0 = OpTypeStruct %v4float + %uint_3 = OpConstant %uint 3 +%_arr_gl_PerVertex_0_uint_3 = OpTypeArray %gl_PerVertex_0 %uint_3 +%_ptr_Input__arr_gl_PerVertex_0_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_0_uint_3 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_0_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %int_1 = OpConstant %int 1 +; CHECK: %_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0 +; CHECK: %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %i %int_0 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %int %i + %18 = OpSLessThan %bool %15 %int_3 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %32 = OpLoad %int %i + %34 = OpAccessChain %_ptr_Input_v4float %gl_in %32 %int_0 + %35 = OpLoad %v4float %34 + %37 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %37 %35 + OpEmitVertex + OpBranch %13 + %13 = OpLabel + %38 = OpLoad %int %i + %40 = OpIAdd %int %38 %int_1 + OpStore %i %40 + OpBranch %10 + %12 = OpLabel + OpEndPrimitive + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true, + false); +} + } // namespace } // namespace opt } // namespace spvtools
diff --git a/test/opt/fold_spec_const_op_composite_test.cpp b/test/opt/fold_spec_const_op_composite_test.cpp index e2374c5..aae9eb2 100644 --- a/test/opt/fold_spec_const_op_composite_test.cpp +++ b/test/opt/fold_spec_const_op_composite_test.cpp
@@ -374,6 +374,33 @@ SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false); } +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, CompositeInsertNull) { + const std::string test = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 +%null = OpConstantNull %mat2v2float + %float_1 = OpConstant %float 1 + %v2float_1 = OpConstantComposite %v2float %float_1 %float_1 + %mat2v2_1 = OpConstantComposite %mat2v2float %v2float_1 %v2float_1 + ; CHECK: %13 = OpConstantNull %mat2v2float + %14 = OpSpecConstantOp %mat2v2float CompositeInsert %mat2v2_1 %null 0 0 + %1 = OpFunction %void None %3 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<FoldSpecConstantOpAndCompositePass>(test, false); +} + // All types and some common constants that are potentially required in // FoldSpecConstantOpAndCompositeTest. std::vector<std::string> CommonTypesAndConstants() {