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() {