[llvm] dbd00a5 - [SPIRV] Improve type inference of operand presented by opaque pointers and aggregate types (#98035)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 10 22:16:34 PDT 2024


Author: Vyacheslav Levytskyy
Date: 2024-07-11T07:16:29+02:00
New Revision: dbd00a5968d6c823d686714c91f2b4fcfd03797a

URL: https://github.com/llvm/llvm-project/commit/dbd00a5968d6c823d686714c91f2b4fcfd03797a
DIFF: https://github.com/llvm/llvm-project/commit/dbd00a5968d6c823d686714c91f2b4fcfd03797a.diff

LOG: [SPIRV] Improve type inference of operand presented by opaque pointers and aggregate types (#98035)

This PR improves type inference of operand presented by opaque pointers
and aggregate types:
* tries to restore original function return type for aggregate types so
that it's possible to deduce a correct type during emit-intrinsics step
(see llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll for the
reproducer of the previously existed issue when spirv-val found a
mismatch between object and ptr types in OpStore due to the incorrect
aggregate types tracing),
* explores untyped pointer operands of store to deduce correct pointee
types,
* creates an extension type to track pointee types from emit-intrinsics
step and further instead of direct and naive usage of TypePointerType
that led previously to crashes due to ban of creation of Value of
TypePointerType type,
* tracks instructions with uncomplete type information and tries to
improve their type info after pass calculated types for all machine
functions (it doesn't traverse a code but rather checks only those
instructions which were tracked as uncompleted),
* address more cases of removing unnecessary bitcasts (see, for example,
changes in test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll where
`CHECK-SPIRV-NEXT` in LIT checks show absence of unneeded bitcasts and
unmangled/mangled versions have proper typing now with equivalent type
info),
* address more cases of well known types or relations between types
within instructions (see, for example, atomic*.ll test cases and
Event-related test cases for improved SPIR-V code generated by the
Backend),
* fix the issue of removing unneeded ptrcast instructions in
pre-legalizer pass that led to creation of new assign-type instructions
with the same argument as source in ptrcast and caused errors in type
inference (the reproducer `complex.ll` test case is added to the PR).

Added: 
    llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll
    llvm/test/CodeGen/SPIRV/instructions/atomic-ptr.ll
    llvm/test/CodeGen/SPIRV/pointers/complex.ll
    llvm/test/CodeGen/SPIRV/pointers/type-deduce-sycl-stub.ll

Modified: 
    llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
    llvm/lib/Target/SPIRV/SPIRVBuiltins.h
    llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
    llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
    llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
    llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
    llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
    llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
    llvm/lib/Target/SPIRV/SPIRVUtils.h
    llvm/test/CodeGen/SPIRV/instructions/atomic.ll
    llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll
    llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll
    llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll
    llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
    llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll
    llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 286bdb9a7ebac..1609576c038d0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -169,21 +169,9 @@ using namespace InstructionSet;
 // TableGen records
 //===----------------------------------------------------------------------===//
 
-/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
-/// the provided \p DemangledCall and specified \p Set.
-///
-/// The lookup follows the following algorithm, returning the first successful
-/// match:
-/// 1. Search with the plain demangled name (expecting a 1:1 match).
-/// 2. Search with the prefix before or suffix after the demangled name
-/// signyfying the type of the first argument.
-///
-/// \returns Wrapper around the demangled call and found builtin definition.
-static std::unique_ptr<const SPIRV::IncomingCall>
-lookupBuiltin(StringRef DemangledCall,
-              SPIRV::InstructionSet::InstructionSet Set,
-              Register ReturnRegister, const SPIRVType *ReturnType,
-              const SmallVectorImpl<Register> &Arguments) {
+namespace SPIRV {
+/// Parses the name part of the demangled builtin call.
+std::string lookupBuiltinNameHelper(StringRef DemangledCall) {
   const static std::string PassPrefix = "(anonymous namespace)::";
   std::string BuiltinName;
   // Itanium Demangler result may have "(anonymous namespace)::" prefix
@@ -215,6 +203,27 @@ lookupBuiltin(StringRef DemangledCall,
     BuiltinName = BuiltinName.substr(0, BuiltinName.find("_R"));
   }
 
+  return BuiltinName;
+}
+} // namespace SPIRV
+
+/// Looks up the demangled builtin call in the SPIRVBuiltins.td records using
+/// the provided \p DemangledCall and specified \p Set.
+///
+/// The lookup follows the following algorithm, returning the first successful
+/// match:
+/// 1. Search with the plain demangled name (expecting a 1:1 match).
+/// 2. Search with the prefix before or suffix after the demangled name
+/// signyfying the type of the first argument.
+///
+/// \returns Wrapper around the demangled call and found builtin definition.
+static std::unique_ptr<const SPIRV::IncomingCall>
+lookupBuiltin(StringRef DemangledCall,
+              SPIRV::InstructionSet::InstructionSet Set,
+              Register ReturnRegister, const SPIRVType *ReturnType,
+              const SmallVectorImpl<Register> &Arguments) {
+  std::string BuiltinName = SPIRV::lookupBuiltinNameHelper(DemangledCall);
+
   SmallVector<StringRef, 10> BuiltinArgumentTypes;
   StringRef BuiltinArgs =
       DemangledCall.slice(DemangledCall.find('(') + 1, DemangledCall.find(')'));
@@ -2610,9 +2619,6 @@ Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall,
     // Unable to recognize SPIRV type name.
     return nullptr;
 
-  if (BaseType->isVoidTy())
-    BaseType = Type::getInt8Ty(Ctx);
-
   // Handle "typeN*" or "type vector[N]*".
   TypeStr.consume_back("*");
 
@@ -2621,7 +2627,8 @@ Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall,
 
   TypeStr.getAsInteger(10, VecElts);
   if (VecElts > 0)
-    BaseType = VectorType::get(BaseType, VecElts, false);
+    BaseType = VectorType::get(
+        BaseType->isVoidTy() ? Type::getInt8Ty(Ctx) : BaseType, VecElts, false);
 
   return BaseType;
 }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
index 68bff602d1d10..d07fc7c6ca874 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
@@ -19,6 +19,8 @@
 
 namespace llvm {
 namespace SPIRV {
+/// Parses the name part of the demangled builtin call.
+std::string lookupBuiltinNameHelper(StringRef DemangledCall);
 /// Lowers a builtin function call using the provided \p DemangledCall skeleton
 /// and external instruction \p Set.
 ///

diff  --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 566eafd41e9bd..d9864ab50ecfe 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -46,6 +46,10 @@
 using namespace llvm;
 
 namespace llvm {
+namespace SPIRV {
+#define GET_BuiltinGroup_DECL
+#include "SPIRVGenTables.inc"
+} // namespace SPIRV
 void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
 } // namespace llvm
 
@@ -69,22 +73,38 @@ class SPIRVEmitIntrinsics
   DenseSet<Instruction *> AggrStores;
   SPIRV::InstructionSet::InstructionSet InstrSet;
 
+  // a register of Instructions that don't have a complete type definition
+  SmallPtrSet<Value *, 8> UncompleteTypeInfo;
+  SmallVector<Instruction *> PostprocessWorklist;
+
+  // well known result types of builtins
+  enum WellKnownTypes { Event };
+
   // deduce element type of untyped pointers
   Type *deduceElementType(Value *I, bool UnknownElemTypeI8);
-  Type *deduceElementTypeHelper(Value *I);
-  Type *deduceElementTypeHelper(Value *I, std::unordered_set<Value *> &Visited);
+  Type *deduceElementTypeHelper(Value *I, bool UnknownElemTypeI8);
+  Type *deduceElementTypeHelper(Value *I, std::unordered_set<Value *> &Visited,
+                                bool UnknownElemTypeI8);
   Type *deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand,
-                                     std::unordered_set<Value *> &Visited);
+                                     bool UnknownElemTypeI8);
+  Type *deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand,
+                                     std::unordered_set<Value *> &Visited,
+                                     bool UnknownElemTypeI8);
   Type *deduceElementTypeByUsersDeep(Value *Op,
-                                     std::unordered_set<Value *> &Visited);
+                                     std::unordered_set<Value *> &Visited,
+                                     bool UnknownElemTypeI8);
+  void maybeAssignPtrType(Type *&Ty, Value *I, Type *RefTy,
+                          bool UnknownElemTypeI8);
 
   // deduce nested types of composites
-  Type *deduceNestedTypeHelper(User *U);
+  Type *deduceNestedTypeHelper(User *U, bool UnknownElemTypeI8);
   Type *deduceNestedTypeHelper(User *U, Type *Ty,
-                               std::unordered_set<Value *> &Visited);
+                               std::unordered_set<Value *> &Visited,
+                               bool UnknownElemTypeI8);
 
   // deduce Types of operands of the Instruction if possible
-  void deduceOperandElementType(Instruction *I);
+  void deduceOperandElementType(Instruction *I, Instruction *AskOp = 0,
+                                Type *AskTy = 0, CallInst *AssignCI = 0);
 
   void preprocessCompositeConstants(IRBuilder<> &B);
   void preprocessUndefs(IRBuilder<> &B);
@@ -151,6 +171,7 @@ class SPIRVEmitIntrinsics
 
   bool runOnModule(Module &M) override;
   bool runOnFunction(Function &F);
+  bool postprocessTypes();
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     ModulePass::getAnalysisUsage(AU);
@@ -223,6 +244,41 @@ static inline void reportFatalOnTokenType(const Instruction *I) {
                        false);
 }
 
+static bool IsKernelArgInt8(Function *F, StoreInst *SI) {
+  return SI && F->getCallingConv() == CallingConv::SPIR_KERNEL &&
+         isPointerTy(SI->getValueOperand()->getType()) &&
+         isa<Argument>(SI->getValueOperand());
+}
+
+// Maybe restore original function return type.
+static inline Type *restoreMutatedType(SPIRVGlobalRegistry *GR, Instruction *I,
+                                       Type *Ty) {
+  CallInst *CI = dyn_cast<CallInst>(I);
+  if (!CI || CI->isIndirectCall() || CI->isInlineAsm() ||
+      !CI->getCalledFunction() || CI->getCalledFunction()->isIntrinsic())
+    return Ty;
+  if (Type *OriginalTy = GR->findMutated(CI->getCalledFunction()))
+    return OriginalTy;
+  return Ty;
+}
+
+// Reconstruct type with nested element types according to deduced type info.
+// Return nullptr if no detailed type info is available.
+static inline Type *reconstructType(SPIRVGlobalRegistry *GR, Value *Op) {
+  Type *Ty = Op->getType();
+  if (!isUntypedPointerTy(Ty))
+    return Ty;
+  // try to find the pointee type
+  if (Type *NestedTy = GR->findDeducedElementType(Op))
+    return getTypedPointerWrapper(NestedTy, getPointerAddressSpace(Ty));
+  // not a pointer according to the type info (e.g., Event object)
+  CallInst *CI = GR->findAssignPtrTypeInstr(Op);
+  if (!CI)
+    return nullptr;
+  MetadataAsValue *MD = cast<MetadataAsValue>(CI->getArgOperand(1));
+  return cast<ConstantAsMetadata>(MD->getMetadata())->getType();
+}
+
 void SPIRVEmitIntrinsics::buildAssignType(IRBuilder<> &B, Type *Ty,
                                           Value *Arg) {
   Value *OfType = PoisonValue::get(Ty);
@@ -263,15 +319,26 @@ void SPIRVEmitIntrinsics::updateAssignType(CallInst *AssignCI, Value *Arg,
 
 // Set element pointer type to the given value of ValueTy and tries to
 // specify this type further (recursively) by Operand value, if needed.
+Type *
+SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand,
+                                                  bool UnknownElemTypeI8) {
+  std::unordered_set<Value *> Visited;
+  return deduceElementTypeByValueDeep(ValueTy, Operand, Visited,
+                                      UnknownElemTypeI8);
+}
+
 Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
-    Type *ValueTy, Value *Operand, std::unordered_set<Value *> &Visited) {
+    Type *ValueTy, Value *Operand, std::unordered_set<Value *> &Visited,
+    bool UnknownElemTypeI8) {
   Type *Ty = ValueTy;
   if (Operand) {
     if (auto *PtrTy = dyn_cast<PointerType>(Ty)) {
-      if (Type *NestedTy = deduceElementTypeHelper(Operand, Visited))
-        Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace());
+      if (Type *NestedTy =
+              deduceElementTypeHelper(Operand, Visited, UnknownElemTypeI8))
+        Ty = getTypedPointerWrapper(NestedTy, PtrTy->getAddressSpace());
     } else {
-      Ty = deduceNestedTypeHelper(dyn_cast<User>(Operand), Ty, Visited);
+      Ty = deduceNestedTypeHelper(dyn_cast<User>(Operand), Ty, Visited,
+                                  UnknownElemTypeI8);
     }
   }
   return Ty;
@@ -279,12 +346,12 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
 
 // Traverse User instructions to deduce an element pointer type of the operand.
 Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep(
-    Value *Op, std::unordered_set<Value *> &Visited) {
+    Value *Op, std::unordered_set<Value *> &Visited, bool UnknownElemTypeI8) {
   if (!Op || !isPointerTy(Op->getType()))
     return nullptr;
 
-  if (auto PType = dyn_cast<TypedPointerType>(Op->getType()))
-    return PType->getElementType();
+  if (auto ElemTy = getPointeeType(Op->getType()))
+    return ElemTy;
 
   // maybe we already know operand's element type
   if (Type *KnownTy = GR->findDeducedElementType(Op))
@@ -292,7 +359,7 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep(
 
   for (User *OpU : Op->users()) {
     if (Instruction *Inst = dyn_cast<Instruction>(OpU)) {
-      if (Type *Ty = deduceElementTypeHelper(Inst, Visited))
+      if (Type *Ty = deduceElementTypeHelper(Inst, Visited, UnknownElemTypeI8))
         return Ty;
     }
   }
@@ -314,13 +381,27 @@ static Type *getPointeeTypeByCallInst(StringRef DemangledName,
 
 // Deduce and return a successfully deduced Type of the Instruction,
 // or nullptr otherwise.
-Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(Value *I) {
+Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(Value *I,
+                                                   bool UnknownElemTypeI8) {
   std::unordered_set<Value *> Visited;
-  return deduceElementTypeHelper(I, Visited);
+  return deduceElementTypeHelper(I, Visited, UnknownElemTypeI8);
+}
+
+void SPIRVEmitIntrinsics::maybeAssignPtrType(Type *&Ty, Value *Op, Type *RefTy,
+                                             bool UnknownElemTypeI8) {
+  if (isUntypedPointerTy(RefTy)) {
+    if (!UnknownElemTypeI8)
+      return;
+    if (auto *I = dyn_cast<Instruction>(Op)) {
+      UncompleteTypeInfo.insert(I);
+      PostprocessWorklist.push_back(I);
+    }
+  }
+  Ty = RefTy;
 }
 
 Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
-    Value *I, std::unordered_set<Value *> &Visited) {
+    Value *I, std::unordered_set<Value *> &Visited, bool UnknownElemTypeI8) {
   // allow to pass nullptr as an argument
   if (!I)
     return nullptr;
@@ -338,34 +419,41 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
   Type *Ty = nullptr;
   // look for known basic patterns of type inference
   if (auto *Ref = dyn_cast<AllocaInst>(I)) {
-    Ty = Ref->getAllocatedType();
+    maybeAssignPtrType(Ty, I, Ref->getAllocatedType(), UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<GetElementPtrInst>(I)) {
     Ty = Ref->getResultElementType();
   } else if (auto *Ref = dyn_cast<GlobalValue>(I)) {
     Ty = deduceElementTypeByValueDeep(
         Ref->getValueType(),
-        Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr, Visited);
+        Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr, Visited,
+        UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<AddrSpaceCastInst>(I)) {
-    Ty = deduceElementTypeHelper(Ref->getPointerOperand(), Visited);
+    Type *RefTy = deduceElementTypeHelper(Ref->getPointerOperand(), Visited,
+                                          UnknownElemTypeI8);
+    maybeAssignPtrType(Ty, I, RefTy, UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<BitCastInst>(I)) {
     if (Type *Src = Ref->getSrcTy(), *Dest = Ref->getDestTy();
         isPointerTy(Src) && isPointerTy(Dest))
-      Ty = deduceElementTypeHelper(Ref->getOperand(0), Visited);
+      Ty = deduceElementTypeHelper(Ref->getOperand(0), Visited,
+                                   UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<AtomicCmpXchgInst>(I)) {
     Value *Op = Ref->getNewValOperand();
-    Ty = deduceElementTypeByValueDeep(Op->getType(), Op, Visited);
+    if (isPointerTy(Op->getType()))
+      Ty = deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<AtomicRMWInst>(I)) {
     Value *Op = Ref->getValOperand();
-    Ty = deduceElementTypeByValueDeep(Op->getType(), Op, Visited);
+    if (isPointerTy(Op->getType()))
+      Ty = deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<PHINode>(I)) {
     for (unsigned i = 0; i < Ref->getNumIncomingValues(); i++) {
-      Ty = deduceElementTypeByUsersDeep(Ref->getIncomingValue(i), Visited);
+      Ty = deduceElementTypeByUsersDeep(Ref->getIncomingValue(i), Visited,
+                                        UnknownElemTypeI8);
       if (Ty)
         break;
     }
   } else if (auto *Ref = dyn_cast<SelectInst>(I)) {
     for (Value *Op : {Ref->getTrueValue(), Ref->getFalseValue()}) {
-      Ty = deduceElementTypeByUsersDeep(Op, Visited);
+      Ty = deduceElementTypeByUsersDeep(Op, Visited, UnknownElemTypeI8);
       if (Ty)
         break;
     }
@@ -384,10 +472,12 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
     if (Function *CalledF = CI->getCalledFunction()) {
       std::string DemangledName =
           getOclOrSpirvBuiltinDemangledName(CalledF->getName());
+      if (DemangledName.length() > 0)
+        DemangledName = SPIRV::lookupBuiltinNameHelper(DemangledName);
       auto AsArgIt = ResTypeByArg.find(DemangledName);
       if (AsArgIt != ResTypeByArg.end()) {
         Ty = deduceElementTypeHelper(CI->getArgOperand(AsArgIt->second),
-                                     Visited);
+                                     Visited, UnknownElemTypeI8);
       }
     }
   }
@@ -404,13 +494,15 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
 // Re-create a type of the value if it has untyped pointer fields, also nested.
 // Return the original value type if no corrections of untyped pointer
 // information is found or needed.
-Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(User *U) {
+Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(User *U,
+                                                  bool UnknownElemTypeI8) {
   std::unordered_set<Value *> Visited;
-  return deduceNestedTypeHelper(U, U->getType(), Visited);
+  return deduceNestedTypeHelper(U, U->getType(), Visited, UnknownElemTypeI8);
 }
 
 Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
-    User *U, Type *OrigTy, std::unordered_set<Value *> &Visited) {
+    User *U, Type *OrigTy, std::unordered_set<Value *> &Visited,
+    bool UnknownElemTypeI8) {
   if (!U)
     return OrigTy;
 
@@ -432,10 +524,12 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
       Type *Ty = OpTy;
       if (Op) {
         if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) {
-          if (Type *NestedTy = deduceElementTypeHelper(Op, Visited))
+          if (Type *NestedTy =
+                  deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8))
             Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace());
         } else {
-          Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited);
+          Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited,
+                                      UnknownElemTypeI8);
         }
       }
       Tys.push_back(Ty);
@@ -451,10 +545,12 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
       Type *OpTy = ArrTy->getElementType();
       Type *Ty = OpTy;
       if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) {
-        if (Type *NestedTy = deduceElementTypeHelper(Op, Visited))
+        if (Type *NestedTy =
+                deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8))
           Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace());
       } else {
-        Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited);
+        Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited,
+                                    UnknownElemTypeI8);
       }
       if (Ty != OpTy) {
         Type *NewTy = ArrayType::get(Ty, ArrTy->getNumElements());
@@ -467,10 +563,12 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
       Type *OpTy = VecTy->getElementType();
       Type *Ty = OpTy;
       if (auto *PtrTy = dyn_cast<PointerType>(OpTy)) {
-        if (Type *NestedTy = deduceElementTypeHelper(Op, Visited))
-          Ty = TypedPointerType::get(NestedTy, PtrTy->getAddressSpace());
+        if (Type *NestedTy =
+                deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8))
+          Ty = getTypedPointerWrapper(NestedTy, PtrTy->getAddressSpace());
       } else {
-        Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited);
+        Ty = deduceNestedTypeHelper(dyn_cast<User>(Op), OpTy, Visited,
+                                    UnknownElemTypeI8);
       }
       if (Ty != OpTy) {
         Type *NewTy = VectorType::get(Ty, VecTy->getElementCount());
@@ -484,16 +582,38 @@ Type *SPIRVEmitIntrinsics::deduceNestedTypeHelper(
 }
 
 Type *SPIRVEmitIntrinsics::deduceElementType(Value *I, bool UnknownElemTypeI8) {
-  if (Type *Ty = deduceElementTypeHelper(I))
+  if (Type *Ty = deduceElementTypeHelper(I, UnknownElemTypeI8))
     return Ty;
-  return UnknownElemTypeI8 ? IntegerType::getInt8Ty(I->getContext()) : nullptr;
+  if (!UnknownElemTypeI8)
+    return nullptr;
+  if (auto *Instr = dyn_cast<Instruction>(I)) {
+    UncompleteTypeInfo.insert(Instr);
+    PostprocessWorklist.push_back(Instr);
+  }
+  return IntegerType::getInt8Ty(I->getContext());
+}
+
+static inline Type *getAtomicElemTy(SPIRVGlobalRegistry *GR, Instruction *I,
+                                    Value *PointerOperand) {
+  Type *PointeeTy = GR->findDeducedElementType(PointerOperand);
+  if (PointeeTy && !isUntypedPointerTy(PointeeTy))
+    return nullptr;
+  auto *PtrTy = dyn_cast<PointerType>(I->getType());
+  if (!PtrTy)
+    return I->getType();
+  if (Type *NestedTy = GR->findDeducedElementType(I))
+    return getTypedPointerWrapper(NestedTy, PtrTy->getAddressSpace());
+  return nullptr;
 }
 
 // If the Instruction has Pointer operands with unresolved types, this function
 // tries to deduce them. If the Instruction has Pointer operands with known
 // types which 
diff er from expected, this function tries to insert a bitcast to
 // resolve the issue.
-void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) {
+void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I,
+                                                   Instruction *AskOp,
+                                                   Type *AskTy,
+                                                   CallInst *AskCI) {
   SmallVector<std::pair<Value *, unsigned>> Ops;
   Type *KnownElemTy = nullptr;
   // look for known basic patterns of type inference
@@ -506,6 +626,51 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) {
       if (isPointerTy(Op->getType()))
         Ops.push_back(std::make_pair(Op, i));
     }
+  } else if (auto *Ref = dyn_cast<AddrSpaceCastInst>(I)) {
+    KnownElemTy = GR->findDeducedElementType(I);
+    if (!KnownElemTy)
+      return;
+    Ops.push_back(std::make_pair(Ref->getPointerOperand(), 0));
+  } else if (auto *Ref = dyn_cast<GetElementPtrInst>(I)) {
+    KnownElemTy = Ref->getSourceElementType();
+    if (isUntypedPointerTy(KnownElemTy))
+      return;
+    Type *PointeeTy = GR->findDeducedElementType(Ref->getPointerOperand());
+    if (PointeeTy && !isUntypedPointerTy(PointeeTy))
+      return;
+    Ops.push_back(std::make_pair(Ref->getPointerOperand(),
+                                 GetElementPtrInst::getPointerOperandIndex()));
+  } else if (auto *Ref = dyn_cast<LoadInst>(I)) {
+    KnownElemTy = I->getType();
+    if (isUntypedPointerTy(KnownElemTy))
+      return;
+    Type *PointeeTy = GR->findDeducedElementType(Ref->getPointerOperand());
+    if (PointeeTy && !isUntypedPointerTy(PointeeTy))
+      return;
+    Ops.push_back(std::make_pair(Ref->getPointerOperand(),
+                                 LoadInst::getPointerOperandIndex()));
+  } else if (auto *Ref = dyn_cast<StoreInst>(I)) {
+    if (IsKernelArgInt8(Ref->getParent()->getParent(), Ref))
+      return;
+    if (!(KnownElemTy = reconstructType(GR, Ref->getValueOperand())))
+      return;
+    Type *PointeeTy = GR->findDeducedElementType(Ref->getPointerOperand());
+    if (PointeeTy && !isUntypedPointerTy(PointeeTy))
+      return;
+    Ops.push_back(std::make_pair(Ref->getPointerOperand(),
+                                 StoreInst::getPointerOperandIndex()));
+  } else if (auto *Ref = dyn_cast<AtomicCmpXchgInst>(I)) {
+    KnownElemTy = getAtomicElemTy(GR, I, Ref->getPointerOperand());
+    if (!KnownElemTy)
+      return;
+    Ops.push_back(std::make_pair(Ref->getPointerOperand(),
+                                 AtomicCmpXchgInst::getPointerOperandIndex()));
+  } else if (auto *Ref = dyn_cast<AtomicRMWInst>(I)) {
+    KnownElemTy = getAtomicElemTy(GR, I, Ref->getPointerOperand());
+    if (!KnownElemTy)
+      return;
+    Ops.push_back(std::make_pair(Ref->getPointerOperand(),
+                                 AtomicRMWInst::getPointerOperandIndex()));
   } else if (auto *Ref = dyn_cast<SelectInst>(I)) {
     if (!isPointerTy(I->getType()) ||
         !(KnownElemTy = GR->findDeducedElementType(I)))
@@ -565,6 +730,32 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) {
               KnownElemTy = ElemTy; // src will rewrite dest if both are defined
             Ops.push_back(std::make_pair(Op, i));
           }
+        } else if (Grp == SPIRV::Atomic || Grp == SPIRV::AtomicFloating) {
+          if (CI->arg_size() < 2)
+            return;
+          Value *Op = CI->getArgOperand(0);
+          if (!isPointerTy(Op->getType()))
+            return;
+          switch (Opcode) {
+          case SPIRV::OpAtomicLoad:
+          case SPIRV::OpAtomicCompareExchangeWeak:
+          case SPIRV::OpAtomicCompareExchange:
+          case SPIRV::OpAtomicExchange:
+          case SPIRV::OpAtomicIAdd:
+          case SPIRV::OpAtomicISub:
+          case SPIRV::OpAtomicOr:
+          case SPIRV::OpAtomicXor:
+          case SPIRV::OpAtomicAnd:
+          case SPIRV::OpAtomicUMin:
+          case SPIRV::OpAtomicUMax:
+          case SPIRV::OpAtomicSMin:
+          case SPIRV::OpAtomicSMax: {
+            KnownElemTy = getAtomicElemTy(GR, I, Op);
+            if (!KnownElemTy)
+              return;
+            Ops.push_back(std::make_pair(Op, 0));
+          } break;
+          }
         }
       }
     }
@@ -578,17 +769,18 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I) {
   IRBuilder<> B(Ctx);
   for (auto &OpIt : Ops) {
     Value *Op = OpIt.first;
-    if (Op->use_empty())
+    if (Op->use_empty() || (AskOp && Op != AskOp))
       continue;
-    Type *Ty = GR->findDeducedElementType(Op);
+    Type *Ty = AskOp ? AskTy : GR->findDeducedElementType(Op);
     if (Ty == KnownElemTy)
       continue;
-    Value *OpTyVal = Constant::getNullValue(KnownElemTy);
+    Value *OpTyVal = PoisonValue::get(KnownElemTy);
     Type *OpTy = Op->getType();
-    if (!Ty) {
+    if (!Ty || AskTy || isUntypedPointerTy(Ty) ||
+        UncompleteTypeInfo.contains(Op)) {
       GR->addDeducedElementType(Op, KnownElemTy);
       // check if there is existing Intrinsic::spv_assign_ptr_type instruction
-      CallInst *AssignCI = GR->findAssignPtrTypeInstr(Op);
+      CallInst *AssignCI = AskCI ? AskCI : GR->findAssignPtrTypeInstr(Op);
       if (AssignCI == nullptr) {
         Instruction *User = dyn_cast<Instruction>(Op->use_begin()->get());
         setInsertPointSkippingPhis(B, User ? User->getNextNode() : I);
@@ -719,7 +911,7 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
         I->replaceUsesOfWith(Op, CI);
         KeepInst = true;
         AggrConsts[CI] = AggrConst;
-        AggrConstTypes[CI] = deduceNestedTypeHelper(AggrConst);
+        AggrConstTypes[CI] = deduceNestedTypeHelper(AggrConst, false);
       }
     }
     if (!KeepInst)
@@ -864,8 +1056,9 @@ void SPIRVEmitIntrinsics::replacePointerOperandWithPtrCast(
     Pointer = BC->getOperand(0);
 
   // Do not emit spv_ptrcast if Pointer's element type is ExpectedElementType
-  Type *PointerElemTy = deduceElementTypeHelper(Pointer);
-  if (PointerElemTy == ExpectedElementType)
+  Type *PointerElemTy = deduceElementTypeHelper(Pointer, false);
+  if (PointerElemTy == ExpectedElementType ||
+      isEquivalentTypes(PointerElemTy, ExpectedElementType))
     return;
 
   setInsertPointSkippingPhis(B, I);
@@ -930,15 +1123,19 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
                                                          IRBuilder<> &B) {
   // Handle basic instructions:
   StoreInst *SI = dyn_cast<StoreInst>(I);
-  if (SI && F->getCallingConv() == CallingConv::SPIR_KERNEL &&
-      isPointerTy(SI->getValueOperand()->getType()) &&
-      isa<Argument>(SI->getValueOperand())) {
+  if (IsKernelArgInt8(F, SI)) {
     return replacePointerOperandWithPtrCast(
         I, SI->getValueOperand(), IntegerType::getInt8Ty(F->getContext()), 0,
         B);
   } else if (SI) {
-    return replacePointerOperandWithPtrCast(
-        I, SI->getPointerOperand(), SI->getValueOperand()->getType(), 1, B);
+    Value *Op = SI->getValueOperand();
+    Type *OpTy = Op->getType();
+    if (auto *OpI = dyn_cast<Instruction>(Op))
+      OpTy = restoreMutatedType(GR, OpI, OpTy);
+    if (OpTy == Op->getType())
+      OpTy = deduceElementTypeByValueDeep(OpTy, Op, false);
+    return replacePointerOperandWithPtrCast(I, SI->getPointerOperand(), OpTy, 1,
+                                            B);
   } else if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
     return replacePointerOperandWithPtrCast(I, LI->getPointerOperand(),
                                             LI->getType(), 0, B);
@@ -978,7 +1175,7 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
         } else {
           for (User *U : CalledArg->users()) {
             if (Instruction *Inst = dyn_cast<Instruction>(U)) {
-              if ((ElemTy = deduceElementTypeHelper(Inst)) != nullptr)
+              if ((ElemTy = deduceElementTypeHelper(Inst, false)) != nullptr)
                 break;
             }
           }
@@ -1012,7 +1209,7 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
     if (!ExpectedType && !DemangledName.empty())
       ExpectedType = SPIRV::parseBuiltinCallArgumentBaseType(
           DemangledName, OpIdx, I->getContext());
-    if (!ExpectedType)
+    if (!ExpectedType || ExpectedType->isVoidTy())
       continue;
 
     if (ExpectedType->isTargetExtTy())
@@ -1182,7 +1379,7 @@ void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV,
     // Deduce element type and store results in Global Registry.
     // Result is ignored, because TypedPointerType is not supported
     // by llvm IR general logic.
-    deduceElementTypeHelper(&GV);
+    deduceElementTypeHelper(&GV, false);
     Constant *Init = GV.getInitializer();
     Type *Ty = isAggrConstForceInt32(Init) ? B.getInt32Ty() : Init->getType();
     Constant *Const = isAggrConstForceInt32(Init) ? B.getInt32(1) : Init;
@@ -1216,9 +1413,39 @@ bool SPIRVEmitIntrinsics::insertAssignPtrTypeIntrs(Instruction *I,
 
 void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I,
                                                 IRBuilder<> &B) {
+  // TODO: extend the list of functions with known result types
+  static StringMap<unsigned> ResTypeWellKnown = {
+      {"async_work_group_copy", WellKnownTypes::Event},
+      {"async_work_group_strided_copy", WellKnownTypes::Event},
+      {"__spirv_GroupAsyncCopy", WellKnownTypes::Event}};
+
   reportFatalOnTokenType(I);
+
+  bool IsKnown = false;
+  if (auto *CI = dyn_cast<CallInst>(I)) {
+    if (!CI->isIndirectCall() && !CI->isInlineAsm() &&
+        CI->getCalledFunction() && !CI->getCalledFunction()->isIntrinsic()) {
+      Function *CalledF = CI->getCalledFunction();
+      std::string DemangledName =
+          getOclOrSpirvBuiltinDemangledName(CalledF->getName());
+      if (DemangledName.length() > 0)
+        DemangledName = SPIRV::lookupBuiltinNameHelper(DemangledName);
+      auto ResIt = ResTypeWellKnown.find(DemangledName);
+      if (ResIt != ResTypeWellKnown.end()) {
+        IsKnown = true;
+        setInsertPointAfterDef(B, I);
+        switch (ResIt->second) {
+        case WellKnownTypes::Event:
+          buildAssignType(B, TargetExtType::get(I->getContext(), "spirv.Event"),
+                          I);
+          break;
+        }
+      }
+    }
+  }
+
   Type *Ty = I->getType();
-  if (!Ty->isVoidTy() && !isPointerTy(Ty) && requireAssignType(I)) {
+  if (!IsKnown && !Ty->isVoidTy() && !isPointerTy(Ty) && requireAssignType(I)) {
     setInsertPointAfterDef(B, I);
     Type *TypeToAssign = Ty;
     if (auto *II = dyn_cast<IntrinsicInst>(I)) {
@@ -1230,6 +1457,7 @@ void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I,
         TypeToAssign = It->second;
       }
     }
+    TypeToAssign = restoreMutatedType(GR, I, TypeToAssign);
     buildAssignType(B, TypeToAssign, I);
   }
   for (const auto &Op : I->operands()) {
@@ -1343,7 +1571,7 @@ Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
       return KnownTy;
     // try to deduce from the operand itself
     Visited.clear();
-    if (Type *Ty = deduceElementTypeHelper(OpArg, Visited))
+    if (Type *Ty = deduceElementTypeHelper(OpArg, Visited, false))
       return Ty;
     // search in actual parameter's users
     for (User *OpU : OpArg->users()) {
@@ -1351,7 +1579,7 @@ Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
       if (!Inst || Inst == CI)
         continue;
       Visited.clear();
-      if (Type *Ty = deduceElementTypeHelper(Inst, Visited))
+      if (Type *Ty = deduceElementTypeHelper(Inst, Visited, false))
         return Ty;
     }
     // check if it's a formal parameter of the outer function
@@ -1480,12 +1708,39 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
   return true;
 }
 
+// Try to deduce a better type for pointers to untyped ptr.
+bool SPIRVEmitIntrinsics::postprocessTypes() {
+  bool Changed = false;
+  if (!GR)
+    return Changed;
+  for (auto IB = PostprocessWorklist.rbegin(), IE = PostprocessWorklist.rend();
+       IB != IE; ++IB) {
+    CallInst *AssignCI = GR->findAssignPtrTypeInstr(*IB);
+    Type *KnownTy = GR->findDeducedElementType(*IB);
+    if (!KnownTy || !AssignCI || !isa<Instruction>(AssignCI->getArgOperand(0)))
+      continue;
+    Instruction *I = cast<Instruction>(AssignCI->getArgOperand(0));
+    for (User *U : I->users()) {
+      Instruction *Inst = dyn_cast<Instruction>(U);
+      if (!Inst || isa<IntrinsicInst>(Inst))
+        continue;
+      deduceOperandElementType(Inst, I, KnownTy, AssignCI);
+      if (KnownTy != GR->findDeducedElementType(I)) {
+        Changed = true;
+        break;
+      }
+    }
+  }
+  return Changed;
+}
+
 bool SPIRVEmitIntrinsics::runOnModule(Module &M) {
   bool Changed = false;
 
-  for (auto &F : M) {
+  UncompleteTypeInfo.clear();
+  PostprocessWorklist.clear();
+  for (auto &F : M)
     Changed |= runOnFunction(F);
-  }
 
   for (auto &F : M) {
     // check if function parameter types are set
@@ -1497,6 +1752,8 @@ bool SPIRVEmitIntrinsics::runOnModule(Module &M) {
     }
   }
 
+  Changed |= postprocessTypes();
+
   return Changed;
 }
 

diff  --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index a45e1ccd0717f..0e26b38225f7a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -51,6 +51,10 @@ class SPIRVGlobalRegistry {
   // Maps Functions to their calls (in a form of the machine instruction,
   // OpFunctionCall) that happened before the definition is available
   DenseMap<const Function *, SmallPtrSet<MachineInstr *, 8>> ForwardCalls;
+  // map a Function to its original return type before the clone function was
+  // created during substitution of aggregate arguments
+  // (see `SPIRVPrepareFunctions::removeAggregateTypesFromSignature()`)
+  DenseMap<Value *, Type *> MutatedAggRet;
 
   // Look for an equivalent of the newType in the map. Return the equivalent
   // if it's found, otherwise insert newType to the map and return the type.
@@ -163,6 +167,16 @@ class SPIRVGlobalRegistry {
     return It == AssignPtrTypeInstr.end() ? nullptr : It->second;
   }
 
+  // A registry of mutated values
+  // (see `SPIRVPrepareFunctions::removeAggregateTypesFromSignature()`):
+  // - Add a record.
+  void addMutated(Value *Val, Type *Ty) { MutatedAggRet[Val] = Ty; }
+  // - Find a record.
+  Type *findMutated(const Value *Val) {
+    auto It = MutatedAggRet.find(Val);
+    return It == MutatedAggRet.end() ? nullptr : It->second;
+  }
+
   // Deduced element types of untyped pointers and composites:
   // - Add a record to the map of deduced element types.
   void addDeducedElementType(Value *Val, Type *Ty) { DeducedElTys[Val] = Ty; }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 9be736ce88ce4..04def5ef01e7b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -2190,7 +2190,7 @@ bool SPIRVInstructionSelector::selectGlobalValue(
   // FIXME: don't use MachineIRBuilder here, replace it with BuildMI.
   MachineIRBuilder MIRBuilder(I);
   const GlobalValue *GV = I.getOperand(1).getGlobal();
-  Type *GVType = GR.getDeducedGlobalValueType(GV);
+  Type *GVType = toTypedPointer(GR.getDeducedGlobalValueType(GV));
   SPIRVType *PointerBaseType;
   if (GVType->isArrayTy()) {
     SPIRVType *ArrayElementType =

diff  --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 6c7c3af199652..e775f8c57b048 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -138,7 +138,8 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       s16,   s32,   s64,   v2s16, v2s32, v2s64, v3s16,  v3s32,  v3s64,
       v4s16, v4s32, v4s64, v8s16, v8s32, v8s64, v16s16, v16s32, v16s64};
 
-  auto allFloatAndIntScalars = allIntScalars;
+  auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1,
+                                       p2, p3,  p4,  p5,  p6};
 
   auto allPtrs = {p0, p1, p2, p3, p4, p5, p6};
   auto allWritablePtrs = {p0, p1, p3, p4, p5, p6};
@@ -238,7 +239,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       .legalForCartesianProduct(allFloatScalars, allWritablePtrs);
 
   getActionDefinitionsBuilder(G_ATOMICRMW_XCHG)
-      .legalForCartesianProduct(allFloatAndIntScalars, allWritablePtrs);
+      .legalForCartesianProduct(allFloatAndIntScalarsAndPtrs, allWritablePtrs);
 
   getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG_WITH_SUCCESS).lower();
   // TODO: add proper legalization rules.

diff  --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index 0ea2f176565e6..099557a608185 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -151,6 +151,20 @@ foldConstantsIntoIntrinsics(MachineFunction &MF,
     MI->eraseFromParent();
 }
 
+static MachineInstr *findAssignTypeInstr(Register Reg,
+                                         MachineRegisterInfo *MRI) {
+  for (MachineRegisterInfo::use_instr_iterator I = MRI->use_instr_begin(Reg),
+                                               IE = MRI->use_instr_end();
+       I != IE; ++I) {
+    MachineInstr *UseMI = &*I;
+    if ((isSpvIntrinsic(*UseMI, Intrinsic::spv_assign_ptr_type) ||
+         isSpvIntrinsic(*UseMI, Intrinsic::spv_assign_type)) &&
+        UseMI->getOperand(1).getReg() == Reg)
+      return UseMI;
+  }
+  return nullptr;
+}
+
 static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
                            MachineIRBuilder MIB) {
   // Get access to information about available extensions
@@ -177,9 +191,12 @@ static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
           BaseTy, MI, *MF.getSubtarget<SPIRVSubtarget>().getInstrInfo(),
           addressSpaceToStorageClass(MI.getOperand(4).getImm(), *ST));
 
-      // If the bitcast would be redundant, replace all uses with the source
+      // If the ptrcast would be redundant, replace all uses with the source
       // register.
       if (GR->getSPIRVTypeForVReg(Source) == AssignedPtrType) {
+        // Erase Def's assign type instruction if we are going to replace Def.
+        if (MachineInstr *AssignMI = findAssignTypeInstr(Def, MIB.getMRI()))
+          ToErase.push_back(AssignMI);
         MIB.getMRI()->replaceRegWith(Def, Source);
       } else {
         GR->assignSPIRVTypeToVReg(AssignedPtrType, Def, MF);
@@ -224,8 +241,8 @@ static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR,
       case TargetOpcode::G_GLOBAL_VALUE: {
         MIB.setInsertPt(*MI->getParent(), MI);
         const GlobalValue *Global = MI->getOperand(1).getGlobal();
-        Type *ElementTy = GR->getDeducedGlobalValueType(Global);
-        auto *Ty = TypedPointerType::get(toTypedPointer(ElementTy),
+        Type *ElementTy = toTypedPointer(GR->getDeducedGlobalValueType(Global));
+        auto *Ty = TypedPointerType::get(ElementTy,
                                          Global->getType()->getAddressSpace());
         SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB);
         break;

diff  --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 7bee87d7204ed..29b8f8fac98e8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -536,6 +536,11 @@ SPIRVPrepareFunctions::removeAggregateTypesFromSignature(Function *F) {
       CI->mutateFunctionType(NewF->getFunctionType());
     U->replaceUsesOfWith(F, NewF);
   }
+
+  // register the mutation
+  if (RetType != F->getReturnType())
+    TM.getSubtarget<SPIRVSubtarget>(*F).getSPIRVGlobalRegistry()->addMutated(
+        NewF, F->getReturnType());
   return NewF;
 }
 

diff  --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index 12725d6bac14a..c757af6b8aa72 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -108,7 +108,7 @@ Type *parseBasicTypeName(StringRef &TypeName, LLVMContext &Ctx);
 
 // True if this is an instance of TypedPointerType.
 inline bool isTypedPointerTy(const Type *T) {
-  return T->getTypeID() == Type::TypedPointerTyID;
+  return T && T->getTypeID() == Type::TypedPointerTyID;
 }
 
 // True if this is an instance of PointerType.
@@ -153,7 +153,61 @@ inline Type *reconstructFunctionType(Function *F) {
   return FunctionType::get(F->getReturnType(), ArgTys, F->isVarArg());
 }
 
+#define TYPED_PTR_TARGET_EXT_NAME "spirv.$TypedPointerType"
+inline Type *getTypedPointerWrapper(Type *ElemTy, unsigned AS) {
+  return TargetExtType::get(ElemTy->getContext(), TYPED_PTR_TARGET_EXT_NAME,
+                            {ElemTy}, {AS});
+}
+
+inline bool isTypedPointerWrapper(TargetExtType *ExtTy) {
+  return ExtTy->getName() == TYPED_PTR_TARGET_EXT_NAME &&
+         ExtTy->getNumIntParameters() == 1 &&
+         ExtTy->getNumTypeParameters() == 1;
+}
+
+inline Type *applyWrappers(Type *Ty) {
+  if (auto *ExtTy = dyn_cast<TargetExtType>(Ty)) {
+    if (isTypedPointerWrapper(ExtTy))
+      return TypedPointerType::get(applyWrappers(ExtTy->getTypeParameter(0)),
+                                   ExtTy->getIntParameter(0));
+  } else if (auto *VecTy = dyn_cast<VectorType>(Ty)) {
+    Type *ElemTy = VecTy->getElementType();
+    Type *NewElemTy = ElemTy->isTargetExtTy() ? applyWrappers(ElemTy) : ElemTy;
+    if (NewElemTy != ElemTy)
+      return VectorType::get(NewElemTy, VecTy->getElementCount());
+  }
+  return Ty;
+}
+
+inline Type *getPointeeType(Type *Ty) {
+  if (auto PType = dyn_cast<TypedPointerType>(Ty))
+    return PType->getElementType();
+  else if (auto *ExtTy = dyn_cast<TargetExtType>(Ty))
+    if (isTypedPointerWrapper(ExtTy))
+      return applyWrappers(ExtTy->getTypeParameter(0));
+  return nullptr;
+}
+
+inline bool isUntypedEquivalentToTyExt(Type *Ty1, Type *Ty2) {
+  if (!isUntypedPointerTy(Ty1) || !Ty2)
+    return false;
+  if (auto *ExtTy = dyn_cast<TargetExtType>(Ty2))
+    if (isTypedPointerWrapper(ExtTy) &&
+        ExtTy->getTypeParameter(0) ==
+            IntegerType::getInt8Ty(Ty1->getContext()) &&
+        ExtTy->getIntParameter(0) == cast<PointerType>(Ty1)->getAddressSpace())
+      return true;
+  return false;
+}
+
+inline bool isEquivalentTypes(Type *Ty1, Type *Ty2) {
+  return isUntypedEquivalentToTyExt(Ty1, Ty2) ||
+         isUntypedEquivalentToTyExt(Ty2, Ty1);
+}
+
 inline Type *toTypedPointer(Type *Ty) {
+  if (Type *NewTy = applyWrappers(Ty); NewTy != Ty)
+    return NewTy;
   return isUntypedPointerTy(Ty)
              ? TypedPointerType::get(IntegerType::getInt8Ty(Ty->getContext()),
                                      getPointerAddressSpace(Ty))

diff  --git a/llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll b/llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll
new file mode 100644
index 0000000000000..9e91854de1172
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/SpecConstants/restore-spec-type.ll
@@ -0,0 +1,46 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#FloatTy:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#StructTy:]] = OpTypeStruct %[[#FloatTy]]
+; CHECK-DAG: %[[#ArrayTy:]] = OpTypeArray %[[#StructTy]] %[[#]]
+; CHECK-DAG: %[[#Struct7Ty:]] = OpTypeStruct %[[#ArrayTy]]
+; CHECK-DAG: %[[#Void:]] = OpTypeVoid
+; CHECK-DAG: %[[#PtrStructTy:]] = OpTypePointer Generic %[[#StructTy]]
+; CHECK-DAG: %[[#PtrStruct7Ty:]] = OpTypePointer Generic %[[#Struct7Ty]]
+; CHECK-DAG: %[[#FunTy:]] = OpTypeFunction %[[#Void]] %[[#PtrStructTy]] %[[#PtrStruct7Ty]]
+; CHECK-DAG: %[[#Const1:]] = OpConstant %[[#FloatTy]] 1
+; CHECK-DAG: %[[#FPtrStructTy:]] = OpTypePointer Function %[[#StructTy]]
+; CHECK-DAG: %[[#Spec1:]] = OpSpecConstantComposite %[[#StructTy]] %[[#Const1]]
+; CHECK-DAG: %[[#Spec2:]] = OpSpecConstantComposite %[[#ArrayTy]] %[[#Spec1]] %[[#Spec1]]
+; CHECK-DAG: %[[#Spec3:]] = OpSpecConstantComposite %[[#Struct7Ty]] %[[#Spec2]]
+; CHECK: %[[#FunDef:]] = OpFunction %[[#Void]] None %[[#FunTy]]
+; CHECK: %[[#Arg1:]] = OpFunctionParameter %[[#PtrStructTy]]
+; CHECK: %[[#Arg2:]] = OpFunctionParameter %[[#PtrStruct7Ty]]
+; CHECK: %[[#]] = OpVariable %[[#FPtrStructTy]] Function
+; CHECK: OpStore %[[#Arg1]] %[[#Spec1]]
+; CHECK: OpStore %[[#Arg2]] %[[#Spec3]]
+; CHECK: OpFunctionEnd
+
+%Struct = type <{ float }>
+%Struct7 = type [2 x %Struct]
+%Nested = type { %Struct7 }
+
+define spir_kernel void @foo(ptr addrspace(4) %arg1, ptr addrspace(4) %arg2) {
+entry:
+  %var = alloca %Struct
+  %r1 = call %Struct @_Z29__spirv_SpecConstantComposite_1(float 1.0)
+  store %Struct %r1, ptr addrspace(4) %arg1
+  %r2 = call %Struct7 @_Z29__spirv_SpecConstantComposite_2(%Struct %r1, %Struct %r1)
+  %r3 = call %Nested @_Z29__spirv_SpecConstantComposite_3(%Struct7 %r2)
+  store %Nested %r3, ptr addrspace(4) %arg2
+
+  ret void
+}
+
+declare %Struct @_Z29__spirv_SpecConstantComposite_1(float)
+declare %Struct7 @_Z29__spirv_SpecConstantComposite_2(%Struct, %Struct)
+declare %Nested @_Z29__spirv_SpecConstantComposite_3(%Struct7)

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/atomic-ptr.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic-ptr.ll
new file mode 100644
index 0000000000000..86e9be15a7c08
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/atomic-ptr.ll
@@ -0,0 +1,38 @@
+; The goal of the test case is to ensure that the Backend doesn't crash on the stage
+; of type inference. Result SPIR-V is not expected to be valid from the perspective
+; of spirv-val in this case, because there is a 
diff erence of accepted return types
+; between atomicrmw and OpAtomicExchange.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+
+; CHECK-DAG: %[[#LongTy:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#PtrLongTy:]] = OpTypePointer CrossWorkgroup %[[#LongTy]]
+; CHECK-DAG: %[[#IntTy:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#Scope:]] = OpConstant %[[#IntTy]] 1
+; CHECK-DAG: %[[#MemSem:]] = OpConstant %[[#IntTy]] 8
+; CHECK-DAG: %[[#PtrPtrLongTy:]] = OpTypePointer CrossWorkgroup %[[#PtrLongTy]]
+
+; CHECK: OpFunction
+; CHECK: %[[#Arg1:]] = OpFunctionParameter %[[#PtrPtrLongTy]]
+; CHECK: %[[#Arg2:]] = OpFunctionParameter %[[#PtrLongTy]]
+; CHECK: OpAtomicExchange %[[#PtrLongTy]] %[[#Arg1]] %[[#Scope]] %[[#MemSem]] %[[#Arg2]]
+; CHECK: OpFunctionEnd
+
+define dso_local spir_func void @test1(ptr addrspace(1) %arg1, ptr addrspace(1) byval(i64) %arg_ptr) {
+entry:
+  %r = atomicrmw xchg ptr addrspace(1) %arg1, ptr addrspace(1) %arg_ptr acq_rel
+  ret void
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#Arg3:]] = OpFunctionParameter %[[#PtrLongTy]]
+; CHECK: %[[#Arg4:]] = OpFunctionParameter %[[#LongTy]]
+; CHECK: OpAtomicExchange %[[#LongTy]] %[[#Arg3]] %[[#Scope]] %[[#MemSem]] %[[#Arg4]]
+; CHECK: OpFunctionEnd
+
+define dso_local spir_func void @test2(ptr addrspace(1) %arg1, i64 %arg_ptr) {
+entry:
+  %r = atomicrmw xchg ptr addrspace(1) %arg1, i64 %arg_ptr acq_rel
+  ret void
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/atomic.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll
index 8a19fc78238c6..1ccbd5a61067d 100644
--- a/llvm/test/CodeGen/SPIRV/instructions/atomic.ll
+++ b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll
@@ -15,18 +15,19 @@
 ; CHECK-DAG: OpName [[XOR:%.*]] "test_xor"
 
 ; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[PtrI32Ty:%.*]] = OpTypePointer Function [[I32Ty]]
 ; CHECK-DAG: [[I64Ty:%.*]] = OpTypeInt 64 0
+; CHECK-DAG: [[PtrI64Ty:%.*]] = OpTypePointer Generic [[I64Ty]]
 ;; Device scope is encoded with constant 1
 ; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1
 ;; "monotonic" maps to the relaxed memory semantics, encoded with constant 0
 ; CHECK-DAG: [[RELAXED:%.*]] = OpConstantNull [[I32Ty]]
 
 ; CHECK:      [[ADD]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_add(i32* %ptr, i32 %val) {
@@ -35,11 +36,10 @@ define i32 @test_add(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[SUB]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_sub(i32* %ptr, i32 %val) {
@@ -48,11 +48,10 @@ define i32 @test_sub(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[MIN]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_min(i32* %ptr, i32 %val) {
@@ -61,11 +60,10 @@ define i32 @test_min(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[MAX]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_max(i32* %ptr, i32 %val) {
@@ -74,11 +72,10 @@ define i32 @test_max(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[UMIN]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_umin(i32* %ptr, i32 %val) {
@@ -87,11 +84,10 @@ define i32 @test_umin(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[UMAX]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_umax(i32* %ptr, i32 %val) {
@@ -100,11 +96,10 @@ define i32 @test_umax(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[AND]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_and(i32* %ptr, i32 %val) {
@@ -113,11 +108,10 @@ define i32 @test_and(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[OR]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_or(i32* %ptr, i32 %val) {
@@ -126,11 +120,10 @@ define i32 @test_or(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[XOR]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[BC_A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_xor(i32* %ptr, i32 %val) {
@@ -139,13 +132,15 @@ define i32 @test_xor(i32* %ptr, i32 %val) {
 }
 
 ; CHECK: OpFunction
-; CHECK: [[Arg1:%.*]] = OpFunctionParameter
-; CHECK: [[Arg2:%.*]] = OpFunctionParameter
-; CHECK: OpAtomicSMin [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]]
-; CHECK: OpAtomicSMax [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]]
-; CHECK: OpAtomicUMin [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]]
-; CHECK: OpAtomicUMax [[I64Ty]] %[[#]] [[SCOPE]] [[RELAXED]] [[Arg2]]
-; CHECK: OpFunctionEnd
+; CHECK-NEXT: [[Arg1:%.*]] = OpFunctionParameter [[PtrI64Ty]]
+; CHECK-NEXT: [[Arg2:%.*]] = OpFunctionParameter [[I64Ty]]
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpAtomicSMin [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]]
+; CHECK-NEXT: OpAtomicSMax [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]]
+; CHECK-NEXT: OpAtomicUMin [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]]
+; CHECK-NEXT: OpAtomicUMax [[I64Ty]] [[Arg1]] [[SCOPE]] [[RELAXED]] [[Arg2]]
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpFunctionEnd
 define dso_local spir_kernel void @test_wrappers(ptr addrspace(4) %arg, i64 %val) {
   %r1 = call spir_func i64 @__spirv_AtomicSMin(ptr addrspace(4) %arg, i32 1, i32 0, i64 %val)
   %r2 = call spir_func i64 @__spirv_AtomicSMax(ptr addrspace(4) %arg, i32 1, i32 0, i64 %val)

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll
index 950dfe417637f..d0c4531a75b65 100644
--- a/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll
+++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll
@@ -12,17 +12,17 @@
 ; CHECK-DAG: OpName [[XOR:%.*]] "test_xor"
 
 ; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[PtrI32Ty:%.*]] = OpTypePointer Function [[I32Ty]]
 ;; Device scope is encoded with constant 1
 ; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1
 ;; "acq_rel" maps to the constant 8
 ; CHECK-DAG: [[ACQREL:%.*]] = OpConstant [[I32Ty]] 8
 
 ; CHECK:      [[ADD]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_add(i32* %ptr, i32 %val) {
@@ -31,11 +31,10 @@ define i32 @test_add(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[SUB]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_sub(i32* %ptr, i32 %val) {
@@ -44,11 +43,10 @@ define i32 @test_sub(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[MIN]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_min(i32* %ptr, i32 %val) {
@@ -57,11 +55,10 @@ define i32 @test_min(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[MAX]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_max(i32* %ptr, i32 %val) {
@@ -70,11 +67,10 @@ define i32 @test_max(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[UMIN]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_umin(i32* %ptr, i32 %val) {
@@ -83,11 +79,10 @@ define i32 @test_umin(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[UMAX]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_umax(i32* %ptr, i32 %val) {
@@ -96,11 +91,10 @@ define i32 @test_umax(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[AND]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_and(i32* %ptr, i32 %val) {
@@ -109,11 +103,10 @@ define i32 @test_and(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[OR]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_or(i32* %ptr, i32 %val) {
@@ -122,11 +115,10 @@ define i32 @test_or(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[XOR]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[BC_A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_xor(i32* %ptr, i32 %val) {

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll
index f142e012dcb74..fc1d6dafa1b08 100644
--- a/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll
+++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll
@@ -12,17 +12,17 @@
 ; CHECK-DAG: OpName [[XOR:%.*]] "test_xor"
 
 ; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[PtrI32Ty:%.*]] = OpTypePointer Function [[I32Ty]]
 ;; Device scope is encoded with constant 1
 ; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1
 ;; "sequentially consistent" maps to constant 16
 ; CHECK-DAG: [[SEQ:%.*]] = OpConstant [[I32Ty]] 16
 
 ; CHECK:      [[ADD]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_add(i32* %ptr, i32 %val) {
@@ -31,11 +31,10 @@ define i32 @test_add(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[SUB]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_sub(i32* %ptr, i32 %val) {
@@ -44,11 +43,10 @@ define i32 @test_sub(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[MIN]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_min(i32* %ptr, i32 %val) {
@@ -57,11 +55,10 @@ define i32 @test_min(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[MAX]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_max(i32* %ptr, i32 %val) {
@@ -70,11 +67,10 @@ define i32 @test_max(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[UMIN]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_umin(i32* %ptr, i32 %val) {
@@ -83,11 +79,10 @@ define i32 @test_umin(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[UMAX]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_umax(i32* %ptr, i32 %val) {
@@ -96,11 +91,10 @@ define i32 @test_umax(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[AND]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_and(i32* %ptr, i32 %val) {
@@ -109,11 +103,10 @@ define i32 @test_and(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[OR]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_or(i32* %ptr, i32 %val) {
@@ -122,11 +115,10 @@ define i32 @test_or(i32* %ptr, i32 %val) {
 }
 
 ; CHECK:      [[XOR]] = OpFunction [[I32Ty]]
-; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
-; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[PtrI32Ty]]
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter [[I32Ty]]
 ; CHECK-NEXT: OpLabel
-; CHECK-NEXT: [[BC_A:%.*]] = OpBitcast %[[#]] [[A]]
-; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[BC_A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
 ; CHECK-NEXT: OpReturnValue [[R]]
 ; CHECK-NEXT: OpFunctionEnd
 define i32 @test_xor(i32* %ptr, i32 %val) {

diff  --git a/llvm/test/CodeGen/SPIRV/pointers/complex.ll b/llvm/test/CodeGen/SPIRV/pointers/complex.ll
new file mode 100644
index 0000000000000..6253ef24283b6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/complex.ll
@@ -0,0 +1,74 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpName %[[#Foo:]] "foo"
+; CHECK-DAG: OpName %[[#CSQRT:]] "csqrt"
+; CHECK-DAG: %[[#FloatTy:]] = OpTypeFloat 64
+; CHECK-DAG: %[[#StructTy:]] = OpTypeStruct %[[#FloatTy]] %[[#FloatTy]]
+; CHECK-DAG: %[[#GPtrStructTy:]] = OpTypePointer Generic %[[#StructTy]]
+; CHECK-DAG: %[[#FPtrStructTy:]] = OpTypePointer Function %[[#StructTy]]
+; CHECK-DAG: %[[#WrapTy:]] = OpTypeStruct %[[#StructTy]]
+; CHECK-DAG: %[[#Int64Ty:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#Int32Ty:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#C1:]] = OpConstant %[[#Int32Ty]] 1
+; CHECK-DAG: %[[#ArrayTy:]] = OpTypeArray %[[#Int64Ty]] %[[#C1]]
+; CHECK-DAG: %[[#SArrayTy:]] = OpTypeStruct %[[#ArrayTy]]
+; CHECK-DAG: %[[#SSArrayTy:]] = OpTypeStruct %[[#SArrayTy]]
+; CHECK-DAG: %[[#CWPtrComplexTy:]] = OpTypePointer CrossWorkgroup %[[#WrapTy]]
+; CHECK-DAG: %[[#IdTy:]] = OpTypePointer Function %[[#SSArrayTy]]
+; CHECK: %[[#Foo]] = OpFunction
+; CHECK: OpFunctionParameter %[[#CWPtrComplexTy]]
+; CHECK: OpFunctionParameter %[[#IdTy]]
+; CHECK: %[[#CSQRT]] = OpFunction
+; CHECK: OpFunctionParameter %[[#GPtrStructTy]]
+; CHECK: OpFunctionParameter %[[#FPtrStructTy]]
+
+%"class.id" = type { %"class.array" }
+%"class.array" = type { [1 x i64] }
+%"class.complex" = type { { double, double } }
+
+define weak_odr dso_local spir_kernel void @foo(ptr addrspace(1) align 8 %_arg_buf_out1_access, ptr byval(%"class.id") align 8 %_arg_buf_out1_access3) {
+entry:
+  %tmp.i.i = alloca { double, double }, align 8
+  %byval-temp.i.i = alloca { double, double }, align 8
+  %idxvalue = load i64, ptr %_arg_buf_out1_access3, align 8
+  %add.ptr.i = getelementptr inbounds %"class.complex", ptr addrspace(1) %_arg_buf_out1_access, i64 %idxvalue
+  %tmp.ascast.i.i = addrspacecast ptr %tmp.i.i to ptr addrspace(4)
+  %byval-temp.imagp.i.i = getelementptr inbounds i8, ptr %byval-temp.i.i, i64 8
+  store double -1.000000e+00, ptr %byval-temp.i.i, align 8
+  store double 0.000000e+00, ptr %byval-temp.imagp.i.i, align 8
+  call spir_func void @csqrt(ptr addrspace(4) dead_on_unwind writable sret({ double, double }) align 8 %tmp.ascast.i.i, ptr nonnull byval({ double, double }) align 8 %byval-temp.i.i)
+  %tmp.ascast.real.i.i = load double, ptr %tmp.i.i, align 8
+  %tmp.ascast.imagp.i.i = getelementptr inbounds i8, ptr %tmp.i.i, i64 8
+  %tmp.ascast.imag.i.i = load double, ptr %tmp.ascast.imagp.i.i, align 8
+  store double %tmp.ascast.real.i.i, ptr addrspace(1) %add.ptr.i, align 8
+  %dest = getelementptr inbounds i8, ptr addrspace(1) %add.ptr.i, i64 8
+  store double %tmp.ascast.imag.i.i, ptr addrspace(1) %dest, align 8
+  ret void
+}
+
+define weak dso_local spir_func void @csqrt(ptr addrspace(4) dead_on_unwind noalias writable sret({ double, double }) align 8 %agg.result, ptr byval({ double, double }) align 8 %z) {
+entry:
+  %tmp = alloca { double, double }, align 8
+  %byval-temp = alloca { double, double }, align 8
+  %tmp.ascast = addrspacecast ptr %tmp to ptr addrspace(4)
+  %z.ascast.real = load double, ptr %z, align 8
+  %z.ascast.imagp = getelementptr inbounds i8, ptr %z, i64 8
+  %z.ascast.imag = load double, ptr %z.ascast.imagp, align 8
+  %byval-temp.imagp = getelementptr inbounds i8, ptr %byval-temp, i64 8
+  store double %z.ascast.real, ptr %byval-temp, align 8
+  store double %z.ascast.imag, ptr %byval-temp.imagp, align 8
+  call spir_func void @__devicelib_csqrt(ptr addrspace(4) dead_on_unwind writable sret({ double, double }) align 8 %tmp.ascast, ptr nonnull byval({ double, double }) align 8 %byval-temp) #7
+  %tmp.ascast.real = load double, ptr %tmp, align 8
+  %tmp.ascast.imagp = getelementptr inbounds i8, ptr %tmp, i64 8
+  %tmp.ascast.imag = load double, ptr %tmp.ascast.imagp, align 8
+  %agg.result.imagp = getelementptr inbounds i8, ptr addrspace(4) %agg.result, i64 8
+  store double %tmp.ascast.real, ptr addrspace(4) %agg.result, align 8
+  store double %tmp.ascast.imag, ptr addrspace(4) %agg.result.imagp, align 8
+  ret void
+}
+
+declare extern_weak dso_local spir_func void @__devicelib_csqrt(ptr addrspace(4) dead_on_unwind writable sret({ double, double }) align 8, ptr byval({ double, double }) align 8) local_unnamed_addr #3

diff  --git a/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll
index b039f80860daf..f060a97a57296 100644
--- a/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll
+++ b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-by-call-chain.ll
@@ -18,11 +18,8 @@
 ; CHECK-SPIRV-DAG: %[[TyFunPtrLong:.*]] = OpTypeFunction %[[TyVoid]] %[[TyPtrLong]]
 ; CHECK-SPIRV-DAG: %[[TyGenPtrPtrLong:.*]] = OpTypePointer Generic %[[TyGenPtrLong]]
 ; CHECK-SPIRV-DAG: %[[TyFunGenPtrLongLong:.*]] = OpTypeFunction %[[TyVoid]] %[[TyGenPtrLong]] %[[TyLong]]
-; CHECK-SPIRV-DAG: %[[TyChar:.*]] = OpTypeInt 8 0
-; CHECK-SPIRV-DAG: %[[TyGenPtrChar:.*]] = OpTypePointer Generic %[[TyChar]]
-; CHECK-SPIRV-DAG: %[[TyGenPtrPtrChar:.*]] = OpTypePointer Generic %[[TyGenPtrChar]]
-; CHECK-SPIRV-DAG: %[[TyFunPtrGenPtrChar:.*]] = OpTypePointer Function %[[TyGenPtrChar]]
 ; CHECK-SPIRV-DAG: %[[Const3:.*]] = OpConstant %[[TyLong]] 3
+; CHECK-SPIRV-DAG: %[[TyFunPtrGenPtrLong:.*]] = OpTypePointer Function %[[TyGenPtrLong]]
 
 ; CHECK-SPIRV: %[[FunTest]] = OpFunction %[[TyVoid]] None %[[TyFunPtrLong]]
 ; CHECK-SPIRV: %[[ArgCum]] = OpFunctionParameter %[[TyPtrLong]]
@@ -41,10 +38,9 @@
 ; CHECK-SPIRV: %[[StubObj]] = OpFunctionParameter %[[TyGenPtrLong]]
 ; CHECK-SPIRV: %[[MemOrder]] = OpFunctionParameter %[[TyLong]]
 
-; CHECK-SPIRV: %[[ObjectAddr:.*]] = OpVariable %[[TyFunPtrGenPtrChar]] Function
-; CHECK-SPIRV-NEXT: %[[ToGeneric:.*]] = OpPtrCastToGeneric %[[TyGenPtrPtrChar]] %[[ObjectAddr]]
-; CHECK-SPIRV-NEXT: %[[Casted:.*]] = OpBitcast %[[TyGenPtrPtrLong]] %[[ToGeneric]]
-; CHECK-SPIRV-NEXT: OpStore %[[Casted]] %[[StubObj]]
+; CHECK-SPIRV: %[[ObjectAddr:.*]] = OpVariable %[[TyFunPtrGenPtrLong]] Function
+; CHECK-SPIRV-NEXT: %[[ToGeneric:.*]] = OpPtrCastToGeneric %[[TyGenPtrPtrLong]] %[[ObjectAddr]]
+; CHECK-SPIRV-NEXT: OpStore %[[ToGeneric]] %[[StubObj]]
 
 ; CHECK-SPIRV: %[[FooFunc]] = OpFunction %[[TyVoid]] None %[[TyFunGenPtrLongLong]]
 ; CHECK-SPIRV: %[[FooObj]] = OpFunctionParameter %[[TyGenPtrLong]]

diff  --git a/llvm/test/CodeGen/SPIRV/pointers/type-deduce-sycl-stub.ll b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-sycl-stub.ll
new file mode 100644
index 0000000000000..008a474f1c104
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/type-deduce-sycl-stub.ll
@@ -0,0 +1,127 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=all --translator-compatibility-mode --avoid-spirv-capabilities=Shader %s -o - -filetype=obj | spirv-val %}
+
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=all --translator-compatibility-mode --avoid-spirv-capabilities=Shader %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-SPIRV-DAG: OpName %[[#F:]] "finish"
+; CHECK-SPIRV-DAG: OpName %[[#FH:]] "finish_helper"
+; CHECK-SPIRV-DAG: OpName %[[#S:]] "start"
+; CHECK-SPIRV-DAG: OpName %[[#SH:]] "start_helper"
+
+; CHECK-SPIRV-DAG: %[[#Long:]] = OpTypeInt 64 0
+; CHECK-SPIRV-DAG: %[[#FPtrLong:]] = OpTypePointer Function %[[#Long]]
+; CHECK-SPIRV-DAG: %[[#GPtrLong:]] = OpTypePointer Generic %[[#Long]]
+; CHECK-SPIRV-DAG: %[[#C3:]] = OpConstant %[[#]] 3
+; CHECK-SPIRV-DAG: %[[#Array3:]] = OpTypeArray %[[#Long]] %[[#C3]]
+; CHECK-SPIRV-DAG: %[[#PtrArray3:]] = OpTypePointer Generic %[[#Array3]]
+; CHECK-SPIRV-DAG: %[[#FPtrPtrArray3:]] = OpTypePointer Function %[[#PtrArray3]]
+; CHECK-SPIRV-DAG: %[[#GPtrPtrArray3:]] = OpTypePointer Generic %[[#PtrArray3]]
+
+; CHECK-SPIRV: %[[#FH]] = OpFunction
+; CHECK-SPIRV: %[[#Arg1:]] = OpFunctionParameter %[[#PtrArray3]]
+; CHECK-SPIRV: %[[#Arg2:]] = OpFunctionParameter %[[#Long]]
+; CHECK-SPIRV: %[[#GrpIdAddr:]] = OpVariable %[[#FPtrPtrArray3]] Function
+; CHECK-SPIRV: %[[#WIId:]] = OpVariable %[[#FPtrLong]] Function
+; CHECK-SPIRV: %[[#GenGrpIdAddr:]] = OpPtrCastToGeneric %[[#GPtrPtrArray3]] %[[#GrpIdAddr]]
+; CHECK-SPIRV: %[[#GenWIId:]] = OpPtrCastToGeneric %[[#GPtrLong]] %[[#WIId]]
+; CHECK-SPIRV: OpStore %[[#GenGrpIdAddr]] %[[#Arg1]]
+; CHECK-SPIRV: OpStore %[[#GenWIId]] %[[#Arg2]]
+; CHECK-SPIRV: OpReturn
+; CHECK-SPIRV: OpFunctionEnd
+
+ at __spirv_BuiltInWorkgroupId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32
+ at __spirv_BuiltInGlobalLinearId = external dso_local local_unnamed_addr addrspace(1) constant i64, align 8
+ at __spirv_BuiltInWorkgroupSize = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32
+
+define weak_odr dso_local spir_kernel void @foo() {
+entry:
+  call spir_func void @start()
+  call spir_func void @finish()
+  ret void
+}
+
+define dso_local spir_func void @start() {
+entry:
+  %GroupID = alloca [3 x i64], align 8
+  %call.i = tail call spir_func signext i8 @__spirv_SpecConstant(i32 noundef -9145239, i8 noundef signext 0)
+  %cmp.i.not = icmp eq i8 %call.i, 0
+  br i1 %cmp.i.not, label %return, label %if.end
+
+if.end:                                           ; preds = %entry
+  %GroupID.ascast = addrspacecast ptr %GroupID to ptr addrspace(4)
+  %r0 = load <3 x i64>, ptr addrspace(1) @__spirv_BuiltInWorkgroupId, align 32
+  %r1 = extractelement <3 x i64> %r0, i64 0
+  store i64 %r1, ptr %GroupID, align 8
+  %arrayinit.element = getelementptr inbounds i8, ptr %GroupID, i64 8
+  %r2 = extractelement <3 x i64> %r0, i64 1
+  store i64 %r2, ptr %arrayinit.element, align 8
+  %arrayinit.element1 = getelementptr inbounds i8, ptr %GroupID, i64 16
+  %r3 = extractelement <3 x i64> %r0, i64 2
+  store i64 %r3, ptr %arrayinit.element1, align 8
+  %r4 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalLinearId, align 8
+  %r5 = load i64, ptr addrspace(1) @__spirv_BuiltInWorkgroupSize, align 32
+  %r6 = load i64, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @__spirv_BuiltInWorkgroupSize, i64 8), align 8
+  %mul = mul i64 %r5, %r6
+  %r7 = load i64, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @__spirv_BuiltInWorkgroupSize, i64 16), align 16
+  %mul2 = mul i64 %mul, %r7
+  %conv = trunc i64 %mul2 to i32
+  call spir_func void @start_helper(ptr addrspace(4) noundef %GroupID.ascast, i64 noundef %r4, i32 noundef %conv)
+  br label %return
+
+return:                                           ; preds = %if.end, %entry
+  ret void
+}
+
+define dso_local spir_func void @finish() {
+entry:
+  %GroupID = alloca [3 x i64], align 8
+  %call.i = tail call spir_func signext i8 @__spirv_SpecConstant(i32 noundef -9145239, i8 noundef signext 0)
+  %cmp.i.not = icmp eq i8 %call.i, 0
+  br i1 %cmp.i.not, label %return, label %if.end
+
+if.end:                                           ; preds = %entry
+  %GroupID.ascast = addrspacecast ptr %GroupID to ptr addrspace(4)
+  %r0 = load <3 x i64>, ptr addrspace(1) @__spirv_BuiltInWorkgroupId, align 32
+  %r1 = extractelement <3 x i64> %r0, i64 0
+  store i64 %r1, ptr %GroupID, align 8
+  %arrayinit.element = getelementptr inbounds i8, ptr %GroupID, i64 8
+  %r2 = extractelement <3 x i64> %r0, i64 1
+  store i64 %r2, ptr %arrayinit.element, align 8
+  %arrayinit.element1 = getelementptr inbounds i8, ptr %GroupID, i64 16
+  %r3 = extractelement <3 x i64> %r0, i64 2
+  store i64 %r3, ptr %arrayinit.element1, align 8
+  %r4 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalLinearId, align 8
+  call spir_func void @finish_helper(ptr addrspace(4) noundef %GroupID.ascast, i64 noundef %r4)
+  br label %return
+
+return:                                           ; preds = %if.end, %entry
+  ret void
+}
+
+define dso_local spir_func void @start_helper(ptr addrspace(4) noundef %group_id, i64 noundef %wi_id, i32 noundef %wg_size) {
+entry:
+  %group_id.addr = alloca ptr addrspace(4), align 8
+  %wi_id.addr = alloca i64, align 8
+  %wg_size.addr = alloca i32, align 4
+  %group_id.addr.ascast = addrspacecast ptr %group_id.addr to ptr addrspace(4)
+  %wi_id.addr.ascast = addrspacecast ptr %wi_id.addr to ptr addrspace(4)
+  %wg_size.addr.ascast = addrspacecast ptr %wg_size.addr to ptr addrspace(4)
+  store ptr addrspace(4) %group_id, ptr addrspace(4) %group_id.addr.ascast, align 8
+  store i64 %wi_id, ptr addrspace(4) %wi_id.addr.ascast, align 8
+  store i32 %wg_size, ptr addrspace(4) %wg_size.addr.ascast, align 4
+  ret void
+}
+
+define dso_local spir_func void @finish_helper(ptr addrspace(4) noundef %group_id, i64 noundef %wi_id) {
+entry:
+  %group_id.addr = alloca ptr addrspace(4), align 8
+  %wi_id.addr = alloca i64, align 8
+  %group_id.addr.ascast = addrspacecast ptr %group_id.addr to ptr addrspace(4)
+  %wi_id.addr.ascast = addrspacecast ptr %wi_id.addr to ptr addrspace(4)
+  store ptr addrspace(4) %group_id, ptr addrspace(4) %group_id.addr.ascast, align 8
+  store i64 %wi_id, ptr addrspace(4) %wi_id.addr.ascast, align 8
+  ret void
+}
+
+declare dso_local spir_func signext i8 @__spirv_SpecConstant(i32 noundef, i8 noundef signext)

diff  --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
index 8f3f71c5337b4..54b2c78674776 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGenericCastToPtr.ll
@@ -19,26 +19,17 @@
 
 ; Mangling
 
-; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]]
-; CHECK-SPIRV: OpFunctionEnd
-
-; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateCharPtr]]
-; CHECK-SPIRV: OpFunctionEnd
+; CHECK-SPIRV:      OpFunction
+; CHECK-SPIRV:      OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV:      OpFunctionEnd
 
 define spir_kernel void @test1(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
 entry:
@@ -59,6 +50,15 @@ entry:
   ret void
 }
 
+; CHECK-SPIRV:      OpFunction
+; CHECK-SPIRV:      OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV:      OpFunctionEnd
+
 define spir_kernel void @test2(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
 entry:
   %var = alloca i32
@@ -88,26 +88,26 @@ declare spir_func ptr @_Z10to_privatePv(ptr addrspace(4))
 
 ; No mangling
 
-; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]]
-; CHECK-SPIRV: OpFunctionEnd
-
-; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericCharPtr]]
-; CHECK-SPIRV: OpPtrCastToGeneric %[[#GenericIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#GlobalIntPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#LocalCharPtr]]
-; CHECK-SPIRV: OpGenericCastToPtr %[[#PrivateIntPtr]]
-; CHECK-SPIRV: OpFunctionEnd
+; CHECK-SPIRV:      OpFunction
+; CHECK-SPIRV:      OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV:      OpFunctionEnd
+
+; CHECK-SPIRV:      OpFunction
+; CHECK-SPIRV:      OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericCharPtr]]
+; CHECK-SPIRV-NEXT: OpPtrCastToGeneric %[[#GenericIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#GlobalIntPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#LocalCharPtr]]
+; CHECK-SPIRV-NEXT: OpGenericCastToPtr %[[#PrivateIntPtr]]
+; CHECK-SPIRV:      OpFunctionEnd
 
 define spir_kernel void @test3(ptr addrspace(1) %_arg_GlobalA, ptr byval(%id) %_arg_GlobalId, ptr addrspace(3) %_arg_LocalA) {
 entry:

diff  --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll
index 96d6016083f06..efb99dc19eb99 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy-strided.ll
@@ -14,12 +14,10 @@
 ; CHECK-SPIRV-DAG: %[[#GenPtrEventTy:]] = OpTypePointer Generic %[[#EventTy]]
 ; CHECK-SPIRV-DAG: %[[#FunPtrEventTy:]] = OpTypePointer Function %[[#EventTy]]
 ; CHECK-SPIRV: OpFunction
-; CHECK-SPIRV: %[[#Var:]] = OpVariable %[[#]] Function
+; CHECK-SPIRV: %[[#Var:]] = OpVariable %[[#FunPtrEventTy]] Function
 ; CHECK-SPIRV: %[[#ResEvent:]] = OpGroupAsyncCopy %[[#EventTy]] %[[#Scope]] %[[#Null]] %[[#Null]] %[[#Num]] %[[#Stride]] %[[#Null]]
-; CHECK-SPIRV: %[[#VarPtrEvent:]] = OpBitcast %[[#FunPtrEventTy]] %[[#Var]]
-; CHECK-SPIRV: OpStore %[[#VarPtrEvent]] %[[#ResEvent]]
-; CHECK-SPIRV: %[[#VarPtrEvent2:]] = OpBitcast %[[#FunPtrEventTy]] %[[#Var]]
-; CHECK-SPIRV: %[[#PtrEventGen:]] = OpPtrCastToGeneric %[[#]] %[[#VarPtrEvent2]]
+; CHECK-SPIRV: OpStore %[[#Var]] %[[#ResEvent]]
+; CHECK-SPIRV: %[[#PtrEventGen:]] = OpPtrCastToGeneric %[[#GenPtrEventTy]] %[[#Var]]
 ; CHECK-SPIRV: OpGroupWaitEvents %[[#Scope]] %[[#Num]] %[[#PtrEventGen]]
 ; CHECK-SPIRV: OpFunctionEnd
 

diff  --git a/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll b/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll
index df11565ca8180..fcb61911e0d29 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/spirv-event-null.ll
@@ -53,13 +53,13 @@ declare dso_local spir_func target("spirv.Event") @_Z22__spirv_GroupAsyncCopyjPU
 ; CHECK: %[[#BarArg1:]] = OpFunctionParameter %[[#TyPtrSV4_W]]
 ; CHECK: %[[#BarArg2:]] = OpFunctionParameter %[[#TyPtrSV4_CW]]
 ; CHECK: %[[#EventVarBar:]] = OpVariable %[[#TyStructPtr]] Function
+; CHECK: %[[#EventVarBarCasted2:]] = OpBitcast %[[#TyEventPtr]] %[[#EventVarBar]]
 ; CHECK: %[[#SrcBar:]] = OpInBoundsPtrAccessChain %[[#TyPtrSV4_CW]] %[[#BarArg2]] %[[#]]
 ; CHECK-DAG: %[[#BarArg1Casted:]] = OpBitcast %[[#TyPtrV4_W]] %[[#BarArg1]]
 ; CHECK-DAG: %[[#SrcBarCasted:]] = OpBitcast %[[#TyPtrV4_CW]] %[[#SrcBar]]
 ; CHECK: %[[#ResBar:]] = OpGroupAsyncCopy %[[#TyEvent]] %[[#]] %[[#BarArg1Casted]] %[[#SrcBarCasted]] %[[#]] %[[#]] %[[#ConstEvent]]
 ; CHECK: %[[#EventVarBarCasted:]] = OpBitcast %[[#TyEventPtr]] %[[#EventVarBar]]
 ; CHECK: OpStore %[[#EventVarBarCasted]] %[[#ResBar]]
-; CHECK: %[[#EventVarBarCasted2:]] = OpBitcast %[[#TyEventPtr]] %[[#EventVarBar]]
 ; CHECK: %[[#EventVarBarGen:]] = OpPtrCastToGeneric %[[#TyEventPtrGen]] %[[#EventVarBarCasted2]]
 ; CHECK: OpGroupWaitEvents %[[#]] %[[#]] %[[#EventVarBarGen]]
 ; CHECK: OpFunctionEnd


        


More information about the llvm-commits mailing list