Fix up type mismatches. (#2545)

Add functionality to fix-storage-class so that it can fix up mismatched
data types for pointers as well.

Fixes bugs in when fixing up storage class.

Move GenerateCopy to the Pass class to be reused.

The spirv-opt change for #2535.
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index e508c05..751786c 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -563,11 +563,6 @@
 }
 void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
                                      Instruction* new_ptr_inst) {
-  // TODO (s-perron): Keep the def-use manager up to date.  Not done now because
-  // it can cause problems for the |ForEachUse| traversals.  Can be use by
-  // keeping a list of instructions that need updating, and then updating them
-  // in |PropagateObject|.
-
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
@@ -703,74 +698,6 @@
   }
 }
 
-uint32_t CopyPropagateArrays::GenerateCopy(Instruction* object_inst,
-                                           uint32_t new_type_id,
-                                           Instruction* insertion_position) {
-  analysis::TypeManager* type_mgr = context()->get_type_mgr();
-  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
-
-  uint32_t original_type_id = object_inst->type_id();
-  if (original_type_id == new_type_id) {
-    return object_inst->result_id();
-  }
-
-  InstructionBuilder ir_builder(
-      context(), insertion_position,
-      IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
-
-  analysis::Type* original_type = type_mgr->GetType(original_type_id);
-  analysis::Type* new_type = type_mgr->GetType(new_type_id);
-
-  if (const analysis::Array* original_array_type = original_type->AsArray()) {
-    uint32_t original_element_type_id =
-        type_mgr->GetId(original_array_type->element_type());
-
-    analysis::Array* new_array_type = new_type->AsArray();
-    assert(new_array_type != nullptr && "Can't copy an array to a non-array.");
-    uint32_t new_element_type_id =
-        type_mgr->GetId(new_array_type->element_type());
-
-    std::vector<uint32_t> element_ids;
-    const analysis::Constant* length_const =
-        const_mgr->FindDeclaredConstant(original_array_type->LengthId());
-    assert(length_const->AsIntConstant());
-    uint32_t array_length = length_const->AsIntConstant()->GetU32();
-    for (uint32_t i = 0; i < array_length; i++) {
-      Instruction* extract = ir_builder.AddCompositeExtract(
-          original_element_type_id, object_inst->result_id(), {i});
-      element_ids.push_back(
-          GenerateCopy(extract, new_element_type_id, insertion_position));
-    }
-
-    return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
-        ->result_id();
-  } else if (const analysis::Struct* original_struct_type =
-                 original_type->AsStruct()) {
-    analysis::Struct* new_struct_type = new_type->AsStruct();
-
-    const std::vector<const analysis::Type*>& original_types =
-        original_struct_type->element_types();
-    const std::vector<const analysis::Type*>& new_types =
-        new_struct_type->element_types();
-    std::vector<uint32_t> element_ids;
-    for (uint32_t i = 0; i < original_types.size(); i++) {
-      Instruction* extract = ir_builder.AddCompositeExtract(
-          type_mgr->GetId(original_types[i]), object_inst->result_id(), {i});
-      element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]),
-                                         insertion_position));
-    }
-    return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
-        ->result_id();
-  } else {
-    // If we do not have an aggregate type, then we have a problem.  Either we
-    // found multiple instances of the same type, or we are copying to an
-    // incompatible type.  Either way the code is illegal.
-    assert(false &&
-           "Don't know how to copy this type.  Code is likely illegal.");
-  }
-  return 0;
-}
-
 uint32_t CopyPropagateArrays::GetMemberTypeId(
     uint32_t id, const std::vector<uint32_t>& access_chain) const {
   for (uint32_t element_index : access_chain) {
diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h
index eb7cc68..f4314a7 100644
--- a/source/opt/copy_prop_arrays.h
+++ b/source/opt/copy_prop_arrays.h
@@ -217,12 +217,6 @@
   // |original_ptr_inst| to |type_id| and still have valid code.
   bool CanUpdateUses(Instruction* original_ptr_inst, uint32_t type_id);
 
-  // Returns the id whose value is the same as |object_to_copy| except its type
-  // is |new_type_id|.  Any instructions need to generate this value will be
-  // inserted before |insertion_position|.
-  uint32_t GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
-                        Instruction* insertion_position);
-
   // Returns a store to |var_inst| that writes to the entire variable, and is
   // the only store that does so.  Note it does not look through OpAccessChain
   // instruction, so partial stores are not considered.
diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp
index 31e7219..03da0d0 100644
--- a/source/opt/fix_storage_class.cpp
+++ b/source/opt/fix_storage_class.cpp
@@ -14,6 +14,8 @@
 
 #include "fix_storage_class.h"
 
+#include <set>
+
 #include "source/opt/instruction.h"
 #include "source/opt/ir_context.h"
 
@@ -25,12 +27,22 @@
 
   get_module()->ForEachInst([this, &modified](Instruction* inst) {
     if (inst->opcode() == SpvOpVariable) {
-      std::vector<Instruction*> uses;
-      get_def_use_mgr()->ForEachUser(
-          inst, [&uses](Instruction* use) { uses.push_back(use); });
-      for (Instruction* use : uses) {
+      std::set<uint32_t> seen;
+      std::vector<std::pair<Instruction*, uint32_t>> uses;
+      get_def_use_mgr()->ForEachUse(inst,
+                                    [&uses](Instruction* use, uint32_t op_idx) {
+                                      uses.push_back({use, op_idx});
+                                    });
+
+      for (auto& use : uses) {
         modified |= PropagateStorageClass(
-            use, static_cast<SpvStorageClass>(inst->GetSingleWordInOperand(0)));
+            use.first,
+            static_cast<SpvStorageClass>(inst->GetSingleWordInOperand(0)),
+            &seen);
+        assert(seen.empty() && "Seen was not properly reset.");
+        modified |=
+            PropagateType(use.first, inst->type_id(), use.second, &seen);
+        assert(seen.empty() && "Seen was not properly reset.");
       }
     }
   });
@@ -38,13 +50,31 @@
 }
 
 bool FixStorageClass::PropagateStorageClass(Instruction* inst,
-                                            SpvStorageClass storage_class) {
+                                            SpvStorageClass storage_class,
+                                            std::set<uint32_t>* seen) {
   if (!IsPointerResultType(inst)) {
     return false;
   }
 
   if (IsPointerToStorageClass(inst, storage_class)) {
-    return false;
+    if (inst->opcode() == SpvOpPhi) {
+      if (!seen->insert(inst->result_id()).second) {
+        return false;
+      }
+    }
+
+    bool modified = false;
+    std::vector<Instruction*> uses;
+    get_def_use_mgr()->ForEachUser(
+        inst, [&uses](Instruction* use) { uses.push_back(use); });
+    for (Instruction* use : uses) {
+      modified |= PropagateStorageClass(use, storage_class, seen);
+    }
+
+    if (inst->opcode() == SpvOpPhi) {
+      seen->erase(inst->result_id());
+    }
+    return modified;
   }
 
   switch (inst->opcode()) {
@@ -54,7 +84,7 @@
     case SpvOpCopyObject:
     case SpvOpPhi:
     case SpvOpSelect:
-      FixInstruction(inst, storage_class);
+      FixInstructionStorageClass(inst, storage_class, seen);
       return true;
     case SpvOpFunctionCall:
       // We cannot be sure of the actual connection between the storage class
@@ -79,8 +109,9 @@
   }
 }
 
-void FixStorageClass::FixInstruction(Instruction* inst,
-                                     SpvStorageClass storage_class) {
+void FixStorageClass::FixInstructionStorageClass(Instruction* inst,
+                                                 SpvStorageClass storage_class,
+                                                 std::set<uint32_t>* seen) {
   assert(IsPointerResultType(inst) &&
          "The result type of the instruction must be a pointer.");
 
@@ -90,7 +121,7 @@
   get_def_use_mgr()->ForEachUser(
       inst, [&uses](Instruction* use) { uses.push_back(use); });
   for (Instruction* use : uses) {
-    PropagateStorageClass(use, storage_class);
+    PropagateStorageClass(use, storage_class, seen);
   }
 }
 
@@ -128,6 +159,171 @@
   return (result_type->storage_class() == storage_class);
 }
 
+bool FixStorageClass::ChangeResultType(Instruction* inst,
+                                       uint32_t new_type_id) {
+  if (inst->type_id() == new_type_id) {
+    return false;
+  }
+
+  context()->ForgetUses(inst);
+  inst->SetResultType(new_type_id);
+  context()->AnalyzeUses(inst);
+  return true;
+}
+
+bool FixStorageClass::PropagateType(Instruction* inst, uint32_t type_id,
+                                    uint32_t op_idx, std::set<uint32_t>* seen) {
+  assert(type_id != 0 && "Not given a valid type in PropagateType");
+  bool modified = false;
+
+  // If the type of operand |op_idx| forces the result type of |inst| to a
+  // particular type, then we want find that type.
+  uint32_t new_type_id = 0;
+  switch (inst->opcode()) {
+    case SpvOpAccessChain:
+    case SpvOpPtrAccessChain:
+    case SpvOpInBoundsAccessChain:
+    case SpvOpInBoundsPtrAccessChain:
+      if (op_idx == 2) {
+        new_type_id = WalkAccessChainType(inst, type_id);
+      }
+      break;
+    case SpvOpCopyObject:
+      new_type_id = type_id;
+      break;
+    case SpvOpPhi:
+      if (seen->insert(inst->result_id()).second) {
+        new_type_id = type_id;
+      }
+      break;
+    case SpvOpSelect:
+      if (op_idx > 2) {
+        new_type_id = type_id;
+      }
+      break;
+    case SpvOpFunctionCall:
+      // We cannot be sure of the actual connection between the type
+      // of the parameter and the type of the result, so we should not
+      // do anything.  If the result type needs to be fixed, the function call
+      // should be inlined.
+      return false;
+    case SpvOpLoad: {
+      Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+      new_type_id = type_inst->GetSingleWordInOperand(1);
+      break;
+    }
+    case SpvOpStore: {
+      uint32_t obj_id = inst->GetSingleWordInOperand(1);
+      Instruction* obj_inst = get_def_use_mgr()->GetDef(obj_id);
+      uint32_t obj_type_id = obj_inst->type_id();
+
+      uint32_t ptr_id = inst->GetSingleWordInOperand(0);
+      Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id);
+      uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst);
+
+      if (obj_type_id != pointee_type_id) {
+        uint32_t copy_id = GenerateCopy(obj_inst, pointee_type_id, inst);
+        inst->SetInOperand(1, {copy_id});
+        context()->UpdateDefUse(inst);
+      }
+    } break;
+    case SpvOpCopyMemory:
+    case SpvOpCopyMemorySized:
+      // TODO: May need to expand the copy as we do with the stores.
+      break;
+    case SpvOpCompositeConstruct:
+    case SpvOpCompositeExtract:
+    case SpvOpCompositeInsert:
+      // TODO: DXC does not seem to generate code that will require changes to
+      // these opcode.  The can be implemented when they come up.
+      break;
+    case SpvOpImageTexelPointer:
+    case SpvOpBitcast:
+      // Nothing to change for these opcode.  The result type is the same
+      // regardless of the type of the operand.
+      return false;
+    default:
+      // I expect the remaining instructions to act on types that are guaranteed
+      // to be unique, so no change will be necessary.
+      break;
+  }
+
+  // If the operand forces the result type, then make sure the result type
+  // matches, and update the uses of |inst|.  We do not have to check the uses
+  // of |inst| in the result type is not forced because we are only looking for
+  // issue that come from mismatches between function formal and actual
+  // parameters after the function has been inlined.  These parameters are
+  // pointers. Once the type no longer depends on the type of the parameter,
+  // then the types should have be correct.
+  if (new_type_id != 0) {
+    modified = ChangeResultType(inst, new_type_id);
+
+    std::vector<std::pair<Instruction*, uint32_t>> uses;
+    get_def_use_mgr()->ForEachUse(inst,
+                                  [&uses](Instruction* use, uint32_t idx) {
+                                    uses.push_back({use, idx});
+                                  });
+
+    for (auto& use : uses) {
+      PropagateType(use.first, new_type_id, use.second, seen);
+    }
+
+    if (inst->opcode() == SpvOpPhi) {
+      seen->erase(inst->result_id());
+    }
+  }
+  return modified;
+}
+
+uint32_t FixStorageClass::WalkAccessChainType(Instruction* inst, uint32_t id) {
+  uint32_t start_idx = 0;
+  switch (inst->opcode()) {
+    case SpvOpAccessChain:
+    case SpvOpInBoundsAccessChain:
+      start_idx = 1;
+      break;
+    case SpvOpPtrAccessChain:
+    case SpvOpInBoundsPtrAccessChain:
+      start_idx = 2;
+      break;
+    default:
+      assert(false);
+      break;
+  }
+
+  Instruction* orig_type_inst = get_def_use_mgr()->GetDef(id);
+  assert(orig_type_inst->opcode() == SpvOpTypePointer);
+  id = orig_type_inst->GetSingleWordInOperand(1);
+
+  for (uint32_t i = start_idx; i < inst->NumInOperands(); ++i) {
+    Instruction* type_inst = get_def_use_mgr()->GetDef(id);
+    switch (type_inst->opcode()) {
+      case SpvOpTypeArray:
+      case SpvOpTypeRuntimeArray:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeVector:
+        id = type_inst->GetSingleWordInOperand(0);
+        break;
+      case SpvOpTypeStruct: {
+        const analysis::Constant* index_const =
+            context()->get_constant_mgr()->FindDeclaredConstant(
+                inst->GetSingleWordInOperand(i));
+        uint32_t index = index_const->GetU32();
+        id = type_inst->GetSingleWordInOperand(index);
+        break;
+      }
+      default:
+        break;
+    }
+    assert(id != 0 &&
+           "Tried to extract from an object where it cannot be done.");
+  }
+
+  return context()->get_type_mgr()->FindPointerToType(
+      id,
+      static_cast<SpvStorageClass>(orig_type_inst->GetSingleWordInOperand(0)));
+}
+
 // namespace opt
 
 }  // namespace opt
diff --git a/source/opt/fix_storage_class.h b/source/opt/fix_storage_class.h
index c496db6..e72e864 100644
--- a/source/opt/fix_storage_class.h
+++ b/source/opt/fix_storage_class.h
@@ -47,13 +47,19 @@
   // Changes the storage class of the result of |inst| to |storage_class| in
   // appropriate, and propagates the change to the users of |inst| as well.
   // Returns true of any changes were made.
-  bool PropagateStorageClass(Instruction* inst, SpvStorageClass storage_class);
+  // |seen| is used to track OpPhi instructions that should not be processed.
+  bool PropagateStorageClass(Instruction* inst, SpvStorageClass storage_class,
+                             std::set<uint32_t>* seen);
 
   // Changes the storage class of the result of |inst| to |storage_class|.
   // Is it assumed that the result type of |inst| is a pointer type.
   // Propagates the change to the users of |inst| as well.
   // Returns true of any changes were made.
-  void FixInstruction(Instruction* inst, SpvStorageClass storage_class);
+  // |seen| is used to track OpPhi instructions that should not be processed by
+  // |PropagateStorageClass|
+  void FixInstructionStorageClass(Instruction* inst,
+                                  SpvStorageClass storage_class,
+                                  std::set<uint32_t>* seen);
 
   // Changes the storage class of the result of |inst| to |storage_class|.  The
   // result type of |inst| must be a pointer.
@@ -67,6 +73,18 @@
   // |storage_class|.
   bool IsPointerToStorageClass(Instruction* inst,
                                SpvStorageClass storage_class);
+
+  // Change |inst| to match that operand |op_idx| now has type |type_id|, and
+  // adjust any uses of |inst| accordingly. Returns true if the code changed.
+  bool PropagateType(Instruction* inst, uint32_t type_id, uint32_t op_idx,
+                     std::set<uint32_t>* seen);
+
+  // Changes the result type of |inst| to |new_type_id|.
+  bool ChangeResultType(Instruction* inst, uint32_t new_type_id);
+
+  // Returns the type id of the member of the type |id| that would be returned
+  // by following the indices of the access chain instruction |inst|.
+  uint32_t WalkAccessChainType(Instruction* inst, uint32_t id);
 };
 
 }  // namespace opt
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp
index edcd245..80075db 100644
--- a/source/opt/pass.cpp
+++ b/source/opt/pass.cpp
@@ -16,6 +16,7 @@
 
 #include "source/opt/pass.h"
 
+#include "source/opt/ir_builder.h"
 #include "source/opt/iterator.h"
 
 namespace spvtools {
@@ -52,5 +53,72 @@
   return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
 }
 
+uint32_t Pass::GenerateCopy(Instruction* object_inst, uint32_t new_type_id,
+                            Instruction* insertion_position) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+
+  uint32_t original_type_id = object_inst->type_id();
+  if (original_type_id == new_type_id) {
+    return object_inst->result_id();
+  }
+
+  InstructionBuilder ir_builder(
+      context(), insertion_position,
+      IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
+
+  analysis::Type* original_type = type_mgr->GetType(original_type_id);
+  analysis::Type* new_type = type_mgr->GetType(new_type_id);
+
+  if (const analysis::Array* original_array_type = original_type->AsArray()) {
+    uint32_t original_element_type_id =
+        type_mgr->GetId(original_array_type->element_type());
+
+    analysis::Array* new_array_type = new_type->AsArray();
+    assert(new_array_type != nullptr && "Can't copy an array to a non-array.");
+    uint32_t new_element_type_id =
+        type_mgr->GetId(new_array_type->element_type());
+
+    std::vector<uint32_t> element_ids;
+    const analysis::Constant* length_const =
+        const_mgr->FindDeclaredConstant(original_array_type->LengthId());
+    assert(length_const->AsIntConstant());
+    uint32_t array_length = length_const->AsIntConstant()->GetU32();
+    for (uint32_t i = 0; i < array_length; i++) {
+      Instruction* extract = ir_builder.AddCompositeExtract(
+          original_element_type_id, object_inst->result_id(), {i});
+      element_ids.push_back(
+          GenerateCopy(extract, new_element_type_id, insertion_position));
+    }
+
+    return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
+        ->result_id();
+  } else if (const analysis::Struct* original_struct_type =
+                 original_type->AsStruct()) {
+    analysis::Struct* new_struct_type = new_type->AsStruct();
+
+    const std::vector<const analysis::Type*>& original_types =
+        original_struct_type->element_types();
+    const std::vector<const analysis::Type*>& new_types =
+        new_struct_type->element_types();
+    std::vector<uint32_t> element_ids;
+    for (uint32_t i = 0; i < original_types.size(); i++) {
+      Instruction* extract = ir_builder.AddCompositeExtract(
+          type_mgr->GetId(original_types[i]), object_inst->result_id(), {i});
+      element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]),
+                                         insertion_position));
+    }
+    return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
+        ->result_id();
+  } else {
+    // If we do not have an aggregate type, then we have a problem.  Either we
+    // found multiple instances of the same type, or we are copying to an
+    // incompatible type.  Either way the code is illegal.
+    assert(false &&
+           "Don't know how to copy this type.  Code is likely illegal.");
+  }
+  return 0;
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/pass.h b/source/opt/pass.h
index c95f502..0667c3d 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -125,6 +125,12 @@
   // TODO(1841): Handle id overflow.
   uint32_t TakeNextId() { return context_->TakeNextId(); }
 
+  // Returns the id whose value is the same as |object_to_copy| except its type
+  // is |new_type_id|.  Any instructions needed to generate this value will be
+  // inserted before |insertion_position|.
+  uint32_t GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
+                        Instruction* insertion_position);
+
  private:
   MessageConsumer consumer_;  // Message consumer.
 
diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp
index c270605..4c8504a 100644
--- a/test/opt/fix_storage_class_test.cpp
+++ b/test/opt/fix_storage_class_test.cpp
@@ -148,7 +148,7 @@
 
 TEST_F(FixStorageClassTest, FixCopyObject) {
   const std::string text = R"(
-; CHECK: OpCopyObject %_ptr_Workgroup_float
+; CHECK: OpCopyObject %_ptr_Workgroup__struct_17
 ; CHECK: OpAccessChain %_ptr_Workgroup_float
 ; CHECK: OpAccessChain %_ptr_Uniform_float
                OpCapability Shader
@@ -172,8 +172,9 @@
 %_arr_float_uint_10 = OpTypeArray %float %uint_10
 %ptr = OpTypePointer Function %_arr_float_uint_10
 %_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
-  %_struct_5 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
-%_ptr_Workgroup__struct_5 = OpTypePointer Workgroup %_struct_5
+  %_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
+%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17
+%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17
 %_runtimearr_float = OpTypeRuntimeArray %float
   %_struct_7 = OpTypeStruct %_runtimearr_float
 %_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
@@ -183,7 +184,7 @@
          %30 = OpTypeFunction %void
 %_ptr_Function_float = OpTypePointer Function %float
 %_ptr_Uniform_float = OpTypePointer Uniform %float
-          %6 = OpVariable %_ptr_Workgroup__struct_5 Workgroup
+          %6 = OpVariable %_ptr_Workgroup__struct_17 Workgroup
           %8 = OpVariable %_ptr_Uniform__struct_7 Uniform
 %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
 %gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
@@ -191,7 +192,7 @@
           %1 = OpFunction %void None %30
          %38 = OpLabel
          %44 = OpLoad %v3uint %gl_LocalInvocationID
-         %cp = OpCopyObject %_ptr_Function_float %6
+         %cp = OpCopyObject %_ptr_Function__struct_17 %6
          %50 = OpAccessChain %_ptr_Function_float %cp %int_0 %int_0 %int_0
          %51 = OpLoad %float %50
          %52 = OpFMul %float %float_2 %51
@@ -209,7 +210,7 @@
 
 TEST_F(FixStorageClassTest, FixPhiInSelMerge) {
   const std::string text = R"(
-; CHECK: OpPhi %_ptr_Workgroup_float
+; CHECK: OpPhi %_ptr_Workgroup__struct_19
 ; CHECK: OpAccessChain %_ptr_Workgroup_float
 ; CHECK: OpAccessChain %_ptr_Uniform_float
                OpCapability Shader
@@ -237,6 +238,7 @@
 %_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
  %_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
 %_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
+%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19
 %_runtimearr_float = OpTypeRuntimeArray %float
   %_struct_7 = OpTypeStruct %_runtimearr_float
 %_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
@@ -259,7 +261,7 @@
          %32 = OpLabel
                OpBranch %31
          %31 = OpLabel
-         %33 = OpPhi %_ptr_Function_float %28 %30 %29 %32
+         %33 = OpPhi %_ptr_Function__struct_19 %28 %30 %29 %32
          %34 = OpLoad %v3uint %gl_LocalInvocationID
          %35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
          %36 = OpLoad %float %35
@@ -278,7 +280,7 @@
 
 TEST_F(FixStorageClassTest, FixPhiInLoop) {
   const std::string text = R"(
-; CHECK: OpPhi %_ptr_Workgroup_float
+; CHECK: OpPhi %_ptr_Workgroup__struct_19
 ; CHECK: OpAccessChain %_ptr_Workgroup_float
 ; CHECK: OpAccessChain %_ptr_Uniform_float
                OpCapability Shader
@@ -306,6 +308,7 @@
 %_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
  %_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
 %_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
+%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19
 %_runtimearr_float = OpTypeRuntimeArray %float
   %_struct_7 = OpTypeStruct %_runtimearr_float
 %_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
@@ -328,7 +331,7 @@
          %32 = OpLabel
                OpBranch %31
          %31 = OpLabel
-         %33 = OpPhi %_ptr_Function_float %28 %30 %29 %32
+         %33 = OpPhi %_ptr_Function__struct_19 %28 %30 %29 %32
          %34 = OpLoad %v3uint %gl_LocalInvocationID
          %35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
          %36 = OpLoad %float %35
@@ -378,7 +381,7 @@
 
 TEST_F(FixStorageClassTest, FixSelect) {
   const std::string text = R"(
-; CHECK: OpSelect %_ptr_Workgroup_float
+; CHECK: OpSelect %_ptr_Workgroup__struct_19
 ; CHECK: OpAccessChain %_ptr_Workgroup_float
 ; CHECK: OpAccessChain %_ptr_Uniform_float
                OpCapability Shader
@@ -406,6 +409,7 @@
 %_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
  %_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
 %_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19
+%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19
 %_runtimearr_float = OpTypeRuntimeArray %float
   %_struct_7 = OpTypeStruct %_runtimearr_float
 %_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
@@ -423,7 +427,7 @@
 %gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input
           %1 = OpFunction %void None %25
          %30 = OpLabel
-         %33 = OpSelect %_ptr_Function_float %true %28 %29
+         %33 = OpSelect %_ptr_Function__struct_19 %true %28 %29
          %34 = OpLoad %v3uint %gl_LocalInvocationID
          %35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0
          %36 = OpLoad %float %35
@@ -459,6 +463,378 @@
   SinglePassRunAndCheck<FixStorageClass>(text, text, false);
 }
 
+TEST_F(FixStorageClassTest, FixLinkedAccessChain2) {
+  // This case is similar to FixLinkedAccessChain.  The difference is that the
+  // first OpAccessChain instruction starts as workgroup storage class.  Only
+  // the second one needs to change.
+  const std::string text = R"(
+; CHECK: OpAccessChain %_ptr_Workgroup__arr_float_uint_10
+; CHECK: OpAccessChain %_ptr_Workgroup_float
+; CHECK: OpAccessChain %_ptr_Uniform_float
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID
+               OpExecutionMode %1 LocalSize 8 8 1
+               OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+               OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
+               OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 0
+               OpDecorate %_runtimearr_float ArrayStride 4
+               OpMemberDecorate %_struct_7 0 Offset 0
+               OpDecorate %_struct_7 BufferBlock
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %float = OpTypeFloat 32
+    %float_2 = OpConstant %float 2
+       %uint = OpTypeInt 32 0
+    %uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%_ptr_Workgroup__arr_float_uint_10 = OpTypePointer Workgroup %_arr_float_uint_10
+%_ptr = OpTypePointer Function %_arr_float_uint_10
+%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10
+ %_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10
+%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
+     %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+       %void = OpTypeVoid
+         %23 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+         %27 = OpVariable %_ptr_Workgroup__struct_17 Workgroup
+          %5 = OpVariable %_ptr_Uniform__struct_7 Uniform
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input
+          %1 = OpFunction %void None %23
+         %28 = OpLabel
+         %29 = OpLoad %v3uint %gl_LocalInvocationID
+         %30 = OpAccessChain %_ptr_Workgroup__arr_float_uint_10 %27 %int_0 %int_0
+         %31 = OpAccessChain %_ptr_Function_float %30 %int_0
+         %32 = OpLoad %float %31
+         %33 = OpFMul %float %float_2 %32
+               OpStore %31 %33
+         %34 = OpLoad %float %31
+         %35 = OpCompositeExtract %uint %29 0
+         %36 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %35
+               OpStore %36 %34
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FixStorageClass>(text, false);
+}
+
+using FixTypeTest = PassTest<::testing::Test>;
+
+TEST_F(FixTypeTest, FixAccessChain) {
+  const std::string text = R"(
+; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
+; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_T [[ac1]] %int_0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource HLSL 600
+               OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+               OpName %S "S"
+               OpMemberName %S 0 "t"
+               OpName %T "T"
+               OpMemberName %T 0 "a"
+               OpName %A "A"
+               OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
+               OpMemberName %type_ACSBuffer_counter 0 "counter"
+               OpName %counter_var_A "counter.var.A"
+               OpName %main "main"
+               OpName %S_0 "S"
+               OpMemberName %S_0 0 "t"
+               OpName %T_0 "T"
+               OpMemberName %T_0 0 "a"
+               OpDecorate %A DescriptorSet 0
+               OpDecorate %A Binding 0
+               OpDecorate %counter_var_A DescriptorSet 0
+               OpDecorate %counter_var_A Binding 1
+               OpMemberDecorate %T 0 Offset 0
+               OpMemberDecorate %S 0 Offset 0
+               OpDecorate %_runtimearr_S ArrayStride 4
+               OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_S BufferBlock
+               OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
+               OpDecorate %type_ACSBuffer_counter BufferBlock
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+          %T = OpTypeStruct %int
+          %S = OpTypeStruct %T
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+%type_ACSBuffer_counter = OpTypeStruct %int
+%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
+       %void = OpTypeVoid
+         %18 = OpTypeFunction %void
+        %T_0 = OpTypeStruct %int
+        %S_0 = OpTypeStruct %T_0
+%_ptr_Function_S_0 = OpTypePointer Function %S_0
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+%_ptr_Uniform_T = OpTypePointer Uniform %T
+         %22 = OpTypeFunction %T_0 %_ptr_Function_S_0
+%_ptr_Function_T_0 = OpTypePointer Function %T_0
+          %A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
+       %main = OpFunction %void None %18
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_T_0 Function
+         %26 = OpVariable %_ptr_Function_S_0 Function
+         %27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
+         %28 = OpAccessChain %_ptr_Function_T_0 %27 %int_0
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FixStorageClass>(text, false);
+}
+
+TEST_F(FixTypeTest, FixLoad) {
+  const std::string text = R"(
+; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
+; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_T [[ac1]] %int_0
+; CHECK: [[ld:%\w+]] = OpLoad %T [[ac2]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource HLSL 600
+               OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+               OpName %S "S"
+               OpMemberName %S 0 "t"
+               OpName %T "T"
+               OpMemberName %T 0 "a"
+               OpName %A "A"
+               OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
+               OpMemberName %type_ACSBuffer_counter 0 "counter"
+               OpName %counter_var_A "counter.var.A"
+               OpName %main "main"
+               OpName %S_0 "S"
+               OpMemberName %S_0 0 "t"
+               OpName %T_0 "T"
+               OpMemberName %T_0 0 "a"
+               OpDecorate %A DescriptorSet 0
+               OpDecorate %A Binding 0
+               OpDecorate %counter_var_A DescriptorSet 0
+               OpDecorate %counter_var_A Binding 1
+               OpMemberDecorate %T 0 Offset 0
+               OpMemberDecorate %S 0 Offset 0
+               OpDecorate %_runtimearr_S ArrayStride 4
+               OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_S BufferBlock
+               OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
+               OpDecorate %type_ACSBuffer_counter BufferBlock
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+          %T = OpTypeStruct %int
+          %S = OpTypeStruct %T
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+%type_ACSBuffer_counter = OpTypeStruct %int
+%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
+       %void = OpTypeVoid
+         %18 = OpTypeFunction %void
+        %T_0 = OpTypeStruct %int
+        %S_0 = OpTypeStruct %T_0
+%_ptr_Function_S_0 = OpTypePointer Function %S_0
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+%_ptr_Uniform_T = OpTypePointer Uniform %T
+         %22 = OpTypeFunction %T_0 %_ptr_Function_S_0
+%_ptr_Function_T_0 = OpTypePointer Function %T_0
+          %A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
+       %main = OpFunction %void None %18
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_T_0 Function
+         %26 = OpVariable %_ptr_Function_S_0 Function
+         %27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
+         %28 = OpAccessChain %_ptr_Uniform_T %27 %int_0
+         %29 = OpLoad %T_0 %28
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FixStorageClass>(text, false);
+}
+
+TEST_F(FixTypeTest, FixStore) {
+  const std::string text = R"(
+; CHECK: [[ld:%\w+]] = OpLoad %T
+; CHECK: OpStore
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource HLSL 600
+               OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+               OpName %S "S"
+               OpMemberName %S 0 "t"
+               OpName %T "T"
+               OpMemberName %T 0 "a"
+               OpName %A "A"
+               OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
+               OpMemberName %type_ACSBuffer_counter 0 "counter"
+               OpName %counter_var_A "counter.var.A"
+               OpName %main "main"
+               OpName %S_0 "S"
+               OpMemberName %S_0 0 "t"
+               OpName %T_0 "T"
+               OpMemberName %T_0 0 "a"
+               OpDecorate %A DescriptorSet 0
+               OpDecorate %A Binding 0
+               OpDecorate %counter_var_A DescriptorSet 0
+               OpDecorate %counter_var_A Binding 1
+               OpMemberDecorate %T 0 Offset 0
+               OpMemberDecorate %S 0 Offset 0
+               OpDecorate %_runtimearr_S ArrayStride 4
+               OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_S BufferBlock
+               OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
+               OpDecorate %type_ACSBuffer_counter BufferBlock
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+          %T = OpTypeStruct %int
+          %S = OpTypeStruct %T
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+%type_ACSBuffer_counter = OpTypeStruct %int
+%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
+       %void = OpTypeVoid
+         %18 = OpTypeFunction %void
+        %T_0 = OpTypeStruct %int
+        %S_0 = OpTypeStruct %T_0
+%_ptr_Function_S_0 = OpTypePointer Function %S_0
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+%_ptr_Uniform_T = OpTypePointer Uniform %T
+         %22 = OpTypeFunction %T_0 %_ptr_Function_S_0
+%_ptr_Function_T_0 = OpTypePointer Function %T_0
+          %A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
+       %main = OpFunction %void None %18
+         %24 = OpLabel
+         %25 = OpVariable %_ptr_Function_T_0 Function
+         %26 = OpVariable %_ptr_Function_S_0 Function
+         %27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0
+         %28 = OpAccessChain %_ptr_Uniform_T %27 %int_0
+         %29 = OpLoad %T %28
+               OpStore %25 %29
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FixStorageClass>(text, false);
+}
+
+TEST_F(FixTypeTest, FixSelect) {
+  const std::string text = R"(
+; CHECK: OpSelect %_ptr_Uniform__struct_3
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpSource HLSL 600
+               OpDecorate %2 DescriptorSet 0
+               OpDecorate %2 Binding 0
+               OpMemberDecorate %_struct_3 0 Offset 0
+               OpDecorate %_runtimearr__struct_3 ArrayStride 4
+               OpMemberDecorate %_struct_5 0 Offset 0
+               OpDecorate %_struct_5 BufferBlock
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+  %_struct_3 = OpTypeStruct %uint
+%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
+  %_struct_5 = OpTypeStruct %_runtimearr__struct_3
+%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void
+ %_struct_12 = OpTypeStruct %uint
+%_ptr_Function__struct_12 = OpTypePointer Function %_struct_12
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+       %bool = OpTypeBool
+%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
+          %2 = OpVariable %_ptr_Uniform__struct_5 Uniform
+          %1 = OpFunction %void None %11
+         %17 = OpLabel
+         %18 = OpAccessChain %_ptr_Uniform_uint %2 %uint_0 %uint_0 %uint_0
+         %19 = OpLoad %uint %18
+         %20 = OpSGreaterThan %bool %19 %uint_0
+         %21 = OpAccessChain %_ptr_Uniform__struct_3 %2 %uint_0 %uint_0
+         %22 = OpAccessChain %_ptr_Uniform__struct_3 %2 %uint_0 %uint_1
+         %23 = OpSelect %_ptr_Function__struct_12 %20 %21 %22
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FixStorageClass>(text, false);
+}
+
+TEST_F(FixTypeTest, FixPhiInLoop) {
+  const std::string text = R"(
+; CHECK: [[ac_init:%\w+]] = OpAccessChain %_ptr_Uniform__struct_3
+; CHECK: [[ac_phi:%\w+]] = OpPhi %_ptr_Uniform__struct_3 [[ac_init]] {{%\w+}} [[ac_update:%\w+]] {{%\w+}}
+; CHECK: [[ac_update]] = OpPtrAccessChain %_ptr_Uniform__struct_3 [[ac_phi]] %int_1
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpSource HLSL 600
+               OpDecorate %2 DescriptorSet 0
+               OpDecorate %2 Binding 0
+               OpMemberDecorate %_struct_3 0 Offset 0
+               OpDecorate %_runtimearr__struct_3 ArrayStride 4
+               OpMemberDecorate %_struct_5 0 Offset 0
+               OpDecorate %_struct_5 BufferBlock
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+  %_struct_3 = OpTypeStruct %int
+  %_struct_9 = OpTypeStruct %int
+%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3
+  %_struct_5 = OpTypeStruct %_runtimearr__struct_3
+%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
+       %void = OpTypeVoid
+         %12 = OpTypeFunction %void
+       %bool = OpTypeBool
+%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3
+%_ptr_Function__struct_9 = OpTypePointer Function %_struct_9
+          %2 = OpVariable %_ptr_Uniform__struct_5 Uniform
+          %1 = OpFunction %void None %12
+         %16 = OpLabel
+         %17 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %int_0
+               OpBranch %18
+         %18 = OpLabel
+         %20 = OpPhi %_ptr_Function__struct_9 %17 %16 %21 %22
+         %23 = OpUndef %bool
+               OpLoopMerge %24 %22 None
+               OpBranchConditional %23 %22 %24
+         %22 = OpLabel
+         %21 = OpPtrAccessChain %_ptr_Function__struct_9 %20 %int_1
+               OpBranch %18
+         %24 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<FixStorageClass>(text, false);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools