[spirv-opt] Clone names for new struct in EliminateIODeadComponents (#5016)

diff --git a/source/opt/eliminate_dead_io_components_pass.cpp b/source/opt/eliminate_dead_io_components_pass.cpp
index e430c6d..916fc27 100644
--- a/source/opt/eliminate_dead_io_components_pass.cpp
+++ b/source/opt/eliminate_dead_io_components_pass.cpp
@@ -236,7 +236,12 @@
     }
     type_mgr->AttachDecoration(*dec, &new_struct_ty);
   }
-  analysis::Type* reg_new_var_ty = type_mgr->GetRegisteredType(&new_struct_ty);
+  // Clone name instructions for new struct type
+  analysis::Type* reg_new_str_ty = type_mgr->GetRegisteredType(&new_struct_ty);
+  uint32_t new_struct_ty_id = type_mgr->GetTypeInstruction(reg_new_str_ty);
+  context()->CloneNames(old_struct_ty_id, new_struct_ty_id, length);
+  // Attach new type to var
+  analysis::Type* reg_new_var_ty = reg_new_str_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);
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index d4dbbaa..35075de 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -378,6 +378,11 @@
   // having more than one name. This method returns the first one it finds.
   inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index);
 
+  // Copy names from |old_id| to |new_id|. Only copy member name if index is
+  // less than |max_member_index|.
+  inline void CloneNames(const uint32_t old_id, const uint32_t new_id,
+                         const uint32_t max_member_index = UINT32_MAX);
+
   // Sets the message consumer to the given |consumer|. |consumer| which will be
   // invoked every time there is a message to be communicated to the outside.
   void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -1208,6 +1213,25 @@
   return nullptr;
 }
 
+void IRContext::CloneNames(const uint32_t old_id, const uint32_t new_id,
+                           const uint32_t max_member_index) {
+  std::vector<std::unique_ptr<Instruction>> names_to_add;
+  auto names = GetNames(old_id);
+  for (auto n : names) {
+    Instruction* old_name_inst = n.second;
+    if (old_name_inst->opcode() == spv::Op::OpMemberName) {
+      auto midx = old_name_inst->GetSingleWordInOperand(1);
+      if (midx >= max_member_index) continue;
+    }
+    std::unique_ptr<Instruction> new_name_inst(old_name_inst->Clone(this));
+    new_name_inst->SetInOperand(0, {new_id});
+    names_to_add.push_back(std::move(new_name_inst));
+  }
+  // We can't add the new names when we are iterating over name range above.
+  // We can add all the new names now.
+  for (auto& new_name : names_to_add) AddDebug2Inst(std::move(new_name));
+}
+
 }  // namespace opt
 }  // namespace spvtools
 
diff --git a/test/opt/eliminate_dead_io_components_test.cpp b/test/opt/eliminate_dead_io_components_test.cpp
index f175e49..da26cef 100644
--- a/test/opt/eliminate_dead_io_components_test.cpp
+++ b/test/opt/eliminate_dead_io_components_test.cpp
@@ -445,9 +445,9 @@
     %v2float = OpTypeVector %float 2
      %Vertex = OpTypeStruct %v4float %v2float
 ; CHECK: %Vertex = OpTypeStruct %v4float %v2float
-; CHECK: [[sty:%\w+]] = OpTypeStruct %v4float
+; CHECK: %Vertex_0 = OpTypeStruct %v4float
 %_ptr_Input_Vertex = OpTypePointer Input %Vertex
-; CHECK: [[pty:%\w+]] = OpTypePointer Input [[sty]]
+; CHECK: [[pty:%\w+]] = OpTypePointer Input %Vertex_0
       %iVert = OpVariable %_ptr_Input_Vertex Input
 ; CHECK: %iVert = OpVariable [[pty]] Input
         %int = OpTypeInt 32 1