[llvm-branch-commits] [clang] [clang] Implement pointer authentication for C++ virtual functions, v-tables, and VTTs. (PR #93907)

Ahmed Bougacha via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu May 30 17:38:38 PDT 2024


https://github.com/ahmedbougacha created https://github.com/llvm/llvm-project/pull/93907

None

>From a27a5bc38c247379d12f93c25f5aa6f00ecedfd9 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Mon, 27 Sep 2021 08:00:00 -0700
Subject: [PATCH 1/2] [clang] Record the original declaration for which a
 v-table slot was created.

Co-Authored-By: John McCall <rjmccall at apple.com>
---
 clang/include/clang/AST/GlobalDecl.h    |  4 ++
 clang/include/clang/AST/VTableBuilder.h | 29 ++++++++
 clang/include/clang/Basic/Thunk.h       |  5 +-
 clang/lib/AST/VTableBuilder.cpp         | 94 ++++++++++++++++++++++++-
 4 files changed, 128 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/AST/GlobalDecl.h b/clang/include/clang/AST/GlobalDecl.h
index 88abba28c991d..386693cabb1fb 100644
--- a/clang/include/clang/AST/GlobalDecl.h
+++ b/clang/include/clang/AST/GlobalDecl.h
@@ -145,6 +145,10 @@ class GlobalDecl {
            LHS.MultiVersionIndex == RHS.MultiVersionIndex;
   }
 
+  bool operator!=(const GlobalDecl &Other) const {
+    return !(*this == Other);
+  }
+
   void *getAsOpaquePtr() const { return Value.getOpaqueValue(); }
 
   explicit operator bool() const { return getAsOpaquePtr(); }
diff --git a/clang/include/clang/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h
index fbf6c041a1ec1..a5de41dbc22f1 100644
--- a/clang/include/clang/AST/VTableBuilder.h
+++ b/clang/include/clang/AST/VTableBuilder.h
@@ -361,6 +361,10 @@ class VTableContextBase {
 };
 
 class ItaniumVTableContext : public VTableContextBase {
+public:
+  typedef llvm::DenseMap<const CXXMethodDecl *, const CXXMethodDecl *>
+      OriginalMethodMapTy;
+
 private:
 
   /// Contains the index (relative to the vtable address point)
@@ -384,6 +388,10 @@ class ItaniumVTableContext : public VTableContextBase {
     VirtualBaseClassOffsetOffsetsMapTy;
   VirtualBaseClassOffsetOffsetsMapTy VirtualBaseClassOffsetOffsets;
 
+  /// Map from a virtual method to the nearest method in the primary base class
+  /// chain that it overrides.
+  OriginalMethodMapTy OriginalMethodMap;
+
   void computeVTableRelatedInformation(const CXXRecordDecl *RD) override;
 
 public:
@@ -425,6 +433,27 @@ class ItaniumVTableContext : public VTableContextBase {
   CharUnits getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
                                        const CXXRecordDecl *VBase);
 
+  /// Return the method that added the v-table slot that will be used to call
+  /// the given method.
+  ///
+  /// In the Itanium ABI, where overrides always cause methods to be added to
+  /// the primary v-table if they're not already there, this will be the first
+  /// declaration in the primary base class chain for which the return type
+  /// adjustment is trivial.
+  GlobalDecl findOriginalMethod(GlobalDecl GD);
+
+  const CXXMethodDecl *findOriginalMethodInMap(const CXXMethodDecl *MD) const;
+
+  void setOriginalMethod(const CXXMethodDecl *Key, const CXXMethodDecl *Val) {
+    OriginalMethodMap[Key] = Val;
+  }
+
+  /// This method is reserved for the implementation and shouldn't be used
+  /// directly.
+  const OriginalMethodMapTy &getOriginalMethodMap() {
+    return OriginalMethodMap;
+  }
+
   static bool classof(const VTableContextBase *VT) {
     return !VT->isMicrosoft();
   }
diff --git a/clang/include/clang/Basic/Thunk.h b/clang/include/clang/Basic/Thunk.h
index 0247e279408f0..fac78f96d1f71 100644
--- a/clang/include/clang/Basic/Thunk.h
+++ b/clang/include/clang/Basic/Thunk.h
@@ -162,7 +162,10 @@ struct ThunkInfo {
 
   /// Holds a pointer to the overridden method this thunk is for,
   /// if needed by the ABI to distinguish different thunks with equal
-  /// adjustments. Otherwise, null.
+  /// adjustments.
+  /// In the Itanium ABI, this field can hold the method that created the
+  /// vtable entry for this thunk.
+  /// Otherwise, null.
   /// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using
   /// an ABI-specific comparator.
   const CXXMethodDecl *Method;
diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp
index a956ca5b37acf..7ec04d1d20546 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -1147,11 +1147,38 @@ void ItaniumVTableBuilder::ComputeThisAdjustments() {
       continue;
 
     // Add it.
-    VTableThunks[VTableIndex].This = ThisAdjustment;
+    auto SetThisAdjustmentThunk = [&](uint64_t Idx) {
+      // If a this pointer adjustment is required, record the method that
+      // created the vtable entry. MD is not necessarily the method that
+      // created the entry since derived classes overwrite base class
+      // information in MethodInfoMap, hence findOriginalMethodInMap is called
+      // here.
+      //
+      // For example, in the following class hierarchy, if MD = D1::m and
+      // Overrider = D2:m, the original method that created the entry is B0:m,
+      // which is what findOriginalMethodInMap(MD) returns:
+      //
+      // struct B0 { int a; virtual void m(); };
+      // struct D0 : B0 { int a; void m() override; };
+      // struct D1 : B0 { int a; void m() override; };
+      // struct D2 : D0, D1 { int a; void m() override; };
+      //
+      // We need to record the method because we cannot
+      // call findOriginalMethod to find the method that created the entry if
+      // the method in the entry requires adjustment.
+      //
+      // Do not set ThunkInfo::Method if Idx is already in VTableThunks. This
+      // can happen when covariant return adjustment is required too.
+      if (!VTableThunks.count(Idx))
+        VTableThunks[Idx].Method = VTables.findOriginalMethodInMap(MD);
+      VTableThunks[Idx].This = ThisAdjustment;
+    };
+
+    SetThisAdjustmentThunk(VTableIndex);
 
     if (isa<CXXDestructorDecl>(MD)) {
       // Add an adjustment for the deleting destructor as well.
-      VTableThunks[VTableIndex + 1].This = ThisAdjustment;
+      SetThisAdjustmentThunk(VTableIndex + 1);
     }
   }
 
@@ -1509,6 +1536,8 @@ void ItaniumVTableBuilder::AddMethods(
           FindNearestOverriddenMethod(MD, PrimaryBases)) {
       if (ComputeReturnAdjustmentBaseOffset(Context, MD,
                                             OverriddenMD).isEmpty()) {
+        VTables.setOriginalMethod(MD, OverriddenMD);
+
         // Replace the method info of the overridden method with our own
         // method.
         assert(MethodInfoMap.count(OverriddenMD) &&
@@ -1615,6 +1644,13 @@ void ItaniumVTableBuilder::AddMethods(
     ReturnAdjustment ReturnAdjustment =
       ComputeReturnAdjustment(ReturnAdjustmentOffset);
 
+    // If a return adjustment is required, record the method that created the
+    // vtable entry. We need to record the method because we cannot call
+    // findOriginalMethod to find the method that created the entry if the
+    // method in the entry requires adjustment.
+    if (!ReturnAdjustment.isEmpty())
+      VTableThunks[Components.size()].Method = MD;
+
     AddMethod(Overrider.Method, ReturnAdjustment);
   }
 }
@@ -1890,11 +1926,32 @@ void ItaniumVTableBuilder::LayoutVTablesForVirtualBases(
   }
 }
 
+static void printThunkMethod(const ThunkInfo &Info, raw_ostream &Out) {
+  if (Info.Method) {
+    std::string Str =
+        PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual,
+                                    Info.Method);
+    Out << " method: " << Str;
+  }
+}
+
 /// dumpLayout - Dump the vtable layout.
 void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
   // FIXME: write more tests that actually use the dumpLayout output to prevent
   // ItaniumVTableBuilder regressions.
 
+  Out << "Original map\n";
+
+  for (const auto &P : VTables.getOriginalMethodMap()) {
+    std::string Str0 =
+        PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual,
+                                    P.first);
+    std::string Str1 =
+        PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual,
+                                    P.second);
+    Out << " " << Str0 << " -> " << Str1 << "\n";
+  }
+
   if (isBuildingConstructorVTable()) {
     Out << "Construction vtable for ('";
     MostDerivedClass->printQualifiedName(Out);
@@ -1978,6 +2035,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
           }
 
           Out << ']';
+          printThunkMethod(Thunk, Out);
         }
 
         // If this function pointer has a 'this' pointer adjustment, dump it.
@@ -1991,6 +2049,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
           }
 
           Out << ']';
+          printThunkMethod(Thunk, Out);
         }
       }
 
@@ -2027,6 +2086,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
 
           Out << ']';
         }
+        printThunkMethod(Thunk, Out);
       }
 
       break;
@@ -2125,7 +2185,6 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
 
       ThunkInfoVectorTy ThunksVector = Thunks[MD];
       llvm::sort(ThunksVector, [](const ThunkInfo &LHS, const ThunkInfo &RHS) {
-        assert(LHS.Method == nullptr && RHS.Method == nullptr);
         return std::tie(LHS.This, LHS.Return) < std::tie(RHS.This, RHS.Return);
       });
 
@@ -2314,6 +2373,35 @@ ItaniumVTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
   return I->second;
 }
 
+GlobalDecl ItaniumVTableContext::findOriginalMethod(GlobalDecl GD) {
+  const auto *MD = cast<CXXMethodDecl>(GD.getDecl());
+  computeVTableRelatedInformation(MD->getParent());
+  const auto *OriginalMD = findOriginalMethodInMap(MD);
+
+  if (const auto *DD = dyn_cast<CXXDestructorDecl>(OriginalMD))
+    return GlobalDecl(DD, GD.getDtorType());
+  return OriginalMD;
+}
+
+const CXXMethodDecl *
+ItaniumVTableContext::findOriginalMethodInMap(const CXXMethodDecl *MD) const {
+  // Traverse the chain of virtual methods until we find the method that added
+  // the v-table slot.
+  while (true) {
+    auto I = OriginalMethodMap.find(MD);
+
+    // MD doesn't exist in OriginalMethodMap, so it must be the method we are
+    // looking for.
+    if (I == OriginalMethodMap.end())
+      break;
+
+    // Set MD to the overridden method.
+    MD = I->second;
+  }
+
+  return MD;
+}
+
 static std::unique_ptr<VTableLayout>
 CreateVTableLayout(const ItaniumVTableBuilder &Builder) {
   SmallVector<VTableLayout::VTableThunkTy, 1>

>From 89bc142513655bc6c9d467f58dbe6835b53d29ac Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Fri, 24 May 2024 16:35:45 -0700
Subject: [PATCH 2/2] [clang] Implement pointer authentication for C++ virtual
 functions, v-tables, and VTTs.

This also implements the ptrauth_vtable_pointer attribute to allow
overriding the default ptrauth schema for vtable pointers.

Co-Authored-By: John McCall <rjmccall at apple.com>
---
 clang/include/clang/AST/ASTContext.h          |  13 +
 clang/include/clang/AST/Mangle.h              |  11 +-
 clang/include/clang/Basic/Attr.td             |  29 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  31 +
 .../include/clang/Basic/PointerAuthOptions.h  |  25 +
 clang/include/clang/Basic/Thunk.h             |   8 +-
 clang/include/clang/CodeGen/CodeGenABITypes.h |   4 +
 .../clang/CodeGen/ConstantInitBuilder.h       |  16 +-
 clang/include/clang/InstallAPI/Visitor.h      |   4 +-
 clang/include/clang/Sema/Sema.h               |   4 +
 clang/lib/AST/ASTContext.cpp                  |  86 +++
 clang/lib/AST/ItaniumMangle.cpp               |  77 ++-
 clang/lib/AST/Mangle.cpp                      |  23 +-
 clang/lib/AST/MicrosoftMangle.cpp             |  23 +-
 clang/lib/AST/VTableBuilder.cpp               |  19 +-
 clang/lib/CodeGen/CGCXX.cpp                   |  11 +-
 clang/lib/CodeGen/CGCXXABI.h                  |  14 +-
 clang/lib/CodeGen/CGClass.cpp                 |  31 +-
 clang/lib/CodeGen/CGExpr.cpp                  |   7 +-
 clang/lib/CodeGen/CGExprConstant.cpp          |  77 ++-
 clang/lib/CodeGen/CGPointerAuth.cpp           | 239 +++++++
 clang/lib/CodeGen/CGVTT.cpp                   |   5 +
 clang/lib/CodeGen/CGVTables.cpp               |  48 +-
 clang/lib/CodeGen/CodeGenFunction.cpp         |  27 +
 clang/lib/CodeGen/CodeGenFunction.h           |  20 +-
 clang/lib/CodeGen/CodeGenModule.h             |  19 +
 clang/lib/CodeGen/ConstantEmitter.h           |   3 +
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  72 +-
 clang/lib/CodeGen/MicrosoftCXXABI.cpp         |   9 +-
 clang/lib/Frontend/CompilerInvocation.cpp     |  12 +
 clang/lib/Headers/ptrauth.h                   |  14 +
 clang/lib/InstallAPI/Visitor.cpp              |  13 +-
 clang/lib/Parse/ParseDecl.cpp                 |  30 +-
 clang/lib/Sema/SemaDeclAttr.cpp               | 116 ++++
 clang/lib/Sema/SemaDeclCXX.cpp                |  46 ++
 clang/lib/Sema/SemaExpr.cpp                   |  33 +
 clang/test/CodeGen/ptrauth-ubsan-vptr.cpp     |  30 +
 .../test/CodeGenCXX/catch-undef-behavior.cpp  |   7 +-
 .../ptrauth-apple-kext-indirect-call-2.cpp    | 105 +++
 .../ptrauth-apple-kext-indirect-call.cpp      |  42 ++
 ...-apple-kext-indirect-virtual-dtor-call.cpp |  50 ++
 ...trauth-explicit-vtable-pointer-control.cpp | 652 ++++++++++++++++++
 clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp |  10 +
 clang/test/CodeGenCXX/ptrauth-thunks.cpp      |  28 +
 .../CodeGenCXX/ptrauth-virtual-function.cpp   | 581 ++++++++++++++++
 ...rauth-vtable-virtual-inheritance-thunk.cpp | 309 +++++++++
 clang/test/CodeGenCXX/ubsan-vtable-checks.cpp |  48 +-
 ...a-attribute-supported-attributes-list.test |   1 +
 ...irtual-member-function-return-arg-type.cpp |  50 ++
 ...table_pointer_authentication_attribute.cpp | 225 ++++++
 clang/utils/TableGen/ClangAttrEmitter.cpp     |  27 +
 51 files changed, 3291 insertions(+), 93 deletions(-)
 create mode 100644 clang/test/CodeGen/ptrauth-ubsan-vptr.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-thunks.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-virtual-function.cpp
 create mode 100644 clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp
 create mode 100644 clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp
 create mode 100644 clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index a1d1d1c51cd41..e601face9c2b6 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -37,6 +37,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/Support/TypeSize.h"
 #include <optional>
@@ -1261,6 +1262,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// space.
   QualType removeAddrSpaceQualType(QualType T) const;
 
+  /// Return the "other" type-specific discriminator for the given type.
+  uint16_t
+  getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *record);
+
   /// Apply Objective-C protocol qualifiers to the given type.
   /// \param allowOnPointerType specifies if we can apply protocol
   /// qualifiers on ObjCObjectPointerType. It can be set to true when
@@ -3418,12 +3423,20 @@ OPT_LIST(V)
   /// Whether a C++ static variable or CUDA/HIP kernel should be externalized.
   bool shouldExternalize(const Decl *D) const;
 
+  /// Resolve the root record to be used to derive the vtable pointer
+  /// authentication policy for the specified record.
+  const CXXRecordDecl *baseForVTableAuthentication(const CXXRecordDecl *);
+  bool useAbbreviatedThunkName(GlobalDecl virtualMethodDecl,
+                               StringRef mangledName);
+
   StringRef getCUIDHash() const;
 
 private:
   /// All OMPTraitInfo objects live in this collection, one per
   /// `pragma omp [begin] declare variant` directive.
   SmallVector<std::unique_ptr<OMPTraitInfo>, 4> OMPTraitInfoVector;
+
+  llvm::DenseMap<GlobalDecl, llvm::StringSet<>> thunksToBeAbbreviated;
 };
 
 /// Insertion operator for diagnostics.
diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h
index e586b0cec43df..d83d56cf42c72 100644
--- a/clang/include/clang/AST/Mangle.h
+++ b/clang/include/clang/AST/Mangle.h
@@ -130,15 +130,15 @@ class MangleContext {
   // FIXME: consider replacing raw_ostream & with something like SmallString &.
   void mangleName(GlobalDecl GD, raw_ostream &);
   virtual void mangleCXXName(GlobalDecl GD, raw_ostream &) = 0;
-  virtual void mangleThunk(const CXXMethodDecl *MD,
-                          const ThunkInfo &Thunk,
-                          raw_ostream &) = 0;
+  virtual void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
+                           bool elideOverrideInfo, raw_ostream &) = 0;
   virtual void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type,
-                                  const ThisAdjustment &ThisAdjustment,
-                                  raw_ostream &) = 0;
+                                  const ThunkInfo &Thunk,
+                                  bool elideOverrideInfo, raw_ostream &) = 0;
   virtual void mangleReferenceTemporary(const VarDecl *D,
                                         unsigned ManglingNumber,
                                         raw_ostream &) = 0;
+  virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0;
   virtual void mangleCXXRTTI(QualType T, raw_ostream &) = 0;
   virtual void mangleCXXRTTIName(QualType T, raw_ostream &,
                                  bool NormalizeIntegers = false) = 0;
@@ -192,7 +192,6 @@ class ItaniumMangleContext : public MangleContext {
                                 bool IsAux = false)
       : MangleContext(C, D, MK_Itanium, IsAux) {}
 
-  virtual void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) = 0;
   virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) = 0;
   virtual void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset,
                                    const CXXRecordDecl *Type,
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index ef9df1e9d8b4a..191afdc147f40 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -680,6 +680,10 @@ class Attr {
   bit PragmaAttributeSupport;
   // Set to true if this attribute accepts parameter pack expansion expressions.
   bit AcceptsExprPack = 0;
+  // To support multiple enum parameters to an attribute without breaking
+  // our existing general parsing we need to have a separate flag that
+  // opts an attribute into strict parsing of attribute parameters
+  bit StrictEnumParameters = 0;
   // Lists language options, one of which is required to be true for the
   // attribute to be applicable. If empty, no language options are required.
   list<LangOpt> LangOpts = [];
@@ -4546,6 +4550,31 @@ def NoRandomizeLayout : InheritableAttr {
 }
 def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
 
+def VTablePointerAuthentication : InheritableAttr {
+  let Spellings = [Clang<"ptrauth_vtable_pointer">];
+  let Subjects = SubjectList<[CXXRecord]>;
+  let Documentation = [Undocumented];
+  let StrictEnumParameters = 1;
+  let Args = [EnumArgument<"Key", "VPtrAuthKeyType", /*is_string=*/ true,
+                ["default_key", "no_authentication", "process_dependent",
+                 "process_independent"],
+                ["DefaultKey", "NoKey", "ProcessDependent",
+                 "ProcessIndependent"]>,
+              EnumArgument<"AddressDiscrimination", "AddressDiscriminationMode",
+                /*is_string=*/ true,
+                ["default_address_discrimination", "no_address_discrimination",
+                 "address_discrimination"],
+                ["DefaultAddressDiscrimination", "NoAddressDiscrimination",
+                 "AddressDiscrimination"]>,
+              EnumArgument<"ExtraDiscrimination", "ExtraDiscrimination",
+                /*is_string=*/ true,
+                ["default_extra_discrimination", "no_extra_discrimination",
+                 "type_discrimination", "custom_discrimination"],
+                ["DefaultExtraDiscrimination", "NoExtraDiscrimination",
+                 "TypeDiscrimination", "CustomDiscrimination"]>,
+              IntArgument<"CustomDiscriminationValue", 1>];
+}
+
 def FunctionReturnThunks : InheritableAttr,
     TargetSpecificAttr<TargetAnyX86> {
   let Spellings = [GCC<"function_return">];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 753e775ce0968..88b8fd344648b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -938,6 +938,13 @@ def warn_ptrauth_auth_null_pointer :
 def err_ptrauth_string_not_literal : Error<
   "argument must be a string literal%select{| of char type}0">;
 
+def note_ptrauth_virtual_function_pointer_incomplete_arg_ret :
+  Note<"cannot take an address of a virtual member function if its return or "
+       "argument types are incomplete">;
+def note_ptrauth_virtual_function_incomplete_arg_ret_type :
+  Note<"%0 is incomplete">;
+
+
 /// main()
 // static main() is not an error in C, just in C++.
 def warn_static_main : Warning<"'main' should not be declared static">,
@@ -12175,6 +12182,30 @@ def warn_cuda_maxclusterrank_sm_90 : Warning<
   "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
   "%1 attribute">, InGroup<IgnoredAttributes>;
 
+// VTable pointer authentication errors
+def err_non_polymorphic_vtable_pointer_auth : Error<
+  "cannot set vtable pointer authentication on monomorphic type %0">;
+def err_incomplete_type_vtable_pointer_auth : Error<
+  "cannot set vtable pointer authentication on an incomplete type %0">;
+def err_non_top_level_vtable_pointer_auth : Error<
+  "cannot set vtable pointer authentication on %0 which is a subclass of polymorphic type %1">;
+def err_duplicated_vtable_pointer_auth : Error<
+  "multiple vtable pointer authentication policies on %0">;
+def err_invalid_authentication_key : Error<
+  "invalid authentication key %0">;
+def err_invalid_address_discrimination : Error<
+  "invalid address discrimination mode %0">;
+def err_invalid_extra_discrimination : Error<
+  "invalid extra discrimination selection %0">;
+def err_invalid_custom_discrimination : Error<
+  "invalid custom discrimination">;
+def err_missing_custom_discrimination : Error<
+  "missing custom discrimination">;
+def err_no_default_vtable_pointer_auth : Error<
+  "cannot specify a default vtable pointer authentication "
+  "%select{key|address discrimination mode|discriminator}0 with no default set"
+>;
+
 def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
                                  "have a bit size of at least %select{2|1}0">;
 def err_bit_int_max_size : Error<"%select{signed|unsigned}0 _BitInt of bit "
diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h
index 32b179e3f9460..d4d794a6fabe0 100644
--- a/clang/include/clang/Basic/PointerAuthOptions.h
+++ b/clang/include/clang/Basic/PointerAuthOptions.h
@@ -49,6 +49,12 @@ class PointerAuthSchema {
     /// No additional discrimination.
     None,
 
+    /// Include a hash of the entity's type.
+    Type,
+
+    /// Include a hash of the entity's identity.
+    Decl,
+
     /// Discriminate using a constant value.
     Constant,
   };
@@ -152,6 +158,25 @@ class PointerAuthSchema {
 struct PointerAuthOptions {
   /// The ABI for C function pointers.
   PointerAuthSchema FunctionPointers;
+
+  /// The ABI for C++ virtual table pointers (the pointer to the table
+  /// itself) as installed in an actual class instance.
+  PointerAuthSchema CXXVTablePointers;
+
+  /// TypeInfo has external ABI requirements and is emitted without
+  /// actually having parsed the libcxx definition, so we can't simply
+  /// perform a look up. The settings for this should match the exact
+  /// specification in type_info.h
+  PointerAuthSchema CXXTypeInfoVTablePointer;
+
+  /// The ABI for C++ virtual table pointers as installed in a VTT.
+  PointerAuthSchema CXXVTTVTablePointers;
+
+  /// The ABI for most C++ virtual function pointers, i.e. v-table entries.
+  PointerAuthSchema CXXVirtualFunctionPointers;
+
+  /// The ABI for variadic C++ virtual function pointers.
+  PointerAuthSchema CXXVirtualVariadicFunctionPointers;
 };
 
 } // end namespace clang
diff --git a/clang/include/clang/Basic/Thunk.h b/clang/include/clang/Basic/Thunk.h
index fac78f96d1f71..4dccebf687585 100644
--- a/clang/include/clang/Basic/Thunk.h
+++ b/clang/include/clang/Basic/Thunk.h
@@ -169,16 +169,20 @@ struct ThunkInfo {
   /// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using
   /// an ABI-specific comparator.
   const CXXMethodDecl *Method;
+  const Type *ThisType { nullptr };
 
   ThunkInfo() : Method(nullptr) {}
 
   ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return,
+            const Type *thisType,
             const CXXMethodDecl *Method = nullptr)
-      : This(This), Return(Return), Method(Method) {}
+      : This(This), Return(Return), Method(Method),
+        ThisType(thisType) {}
 
   friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) {
     return LHS.This == RHS.This && LHS.Return == RHS.Return &&
-           LHS.Method == RHS.Method;
+           LHS.Method == RHS.Method &&
+           LHS.ThisType == RHS.ThisType;
   }
 
   bool isEmpty() const {
diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h
index 8c62d8597ecbe..0d0c152c6fea0 100644
--- a/clang/include/clang/CodeGen/CodeGenABITypes.h
+++ b/clang/include/clang/CodeGen/CodeGenABITypes.h
@@ -42,6 +42,7 @@ class CXXConstructorDecl;
 class CXXDestructorDecl;
 class CXXRecordDecl;
 class CXXMethodDecl;
+class GlobalDecl;
 class ObjCMethodDecl;
 class ObjCProtocolDecl;
 
@@ -104,6 +105,9 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T);
 unsigned getLLVMFieldNumber(CodeGenModule &CGM,
                             const RecordDecl *RD, const FieldDecl *FD);
 
+/// Return a declaration discriminator for the given global decl.
+uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD);
+
 /// Return a signed constant pointer.
 llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM,
                                          llvm::Constant *pointer,
diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h
index 498acfd380131..f6d8e15ff2131 100644
--- a/clang/include/clang/CodeGen/ConstantInitBuilder.h
+++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h
@@ -25,8 +25,11 @@
 #include <vector>
 
 namespace clang {
-namespace CodeGen {
+class GlobalDecl;
+class PointerAuthSchema;
+class QualType;
 
+namespace CodeGen {
 class CodeGenModule;
 
 /// A convenience builder class for complex constant initializers,
@@ -199,6 +202,17 @@ class ConstantAggregateBuilderBase {
     add(llvm::ConstantInt::get(intTy, value, isSigned));
   }
 
+  /// Add a signed pointer using the given pointer authentication schema.
+  void addSignedPointer(llvm::Constant *pointer,
+                        const PointerAuthSchema &schema, GlobalDecl calleeDecl,
+                        QualType calleeType);
+
+  /// Add a signed pointer using the given pointer authentication schema.
+  void addSignedPointer(llvm::Constant *pointer,
+                        unsigned key,
+                        bool useAddressDiscrimination,
+                        llvm::Constant *otherDiscriminator);
+
   /// Add a null pointer of a specific type.
   void addNullPointer(llvm::PointerType *ptrTy) {
     add(llvm::ConstantPointerNull::get(ptrTy));
diff --git a/clang/include/clang/InstallAPI/Visitor.h b/clang/include/clang/InstallAPI/Visitor.h
index 9ac948ded3e33..3680ee566ca87 100644
--- a/clang/include/clang/InstallAPI/Visitor.h
+++ b/clang/include/clang/InstallAPI/Visitor.h
@@ -60,8 +60,8 @@ class InstallAPIVisitor final : public ASTConsumer,
   std::string getMangledName(const NamedDecl *D) const;
   std::string getBackendMangledName(llvm::Twine Name) const;
   std::string getMangledCXXVTableName(const CXXRecordDecl *D) const;
-  std::string getMangledCXXThunk(const GlobalDecl &D,
-                                 const ThunkInfo &Thunk) const;
+  std::string getMangledCXXThunk(const GlobalDecl &D, const ThunkInfo &Thunk,
+                                 bool ElideOverrideInfo) const;
   std::string getMangledCXXRTTI(const CXXRecordDecl *D) const;
   std::string getMangledCXXRTTIName(const CXXRecordDecl *D) const;
   std::string getMangledCtorDtor(const CXXMethodDecl *D, int Type) const;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ec083f7cc09b7..dd65662e77860 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4401,6 +4401,10 @@ class Sema final : public SemaBase {
   /// conditions that are needed for the attribute to have an effect.
   void checkIllFormedTrivialABIStruct(CXXRecordDecl &RD);
 
+  /// Check that VTable Pointer authentication is only being set on the first
+  /// first instantiation of the vtable
+  void checkIncorrectVTablePointerAuthenticationAttribute(CXXRecordDecl &RD);
+
   void ActOnFinishCXXMemberSpecification(Scope *S, SourceLocation RLoc,
                                          Decl *TagDecl, SourceLocation LBrac,
                                          SourceLocation RBrac,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a2398fef623ea..2e230ce556459 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -86,6 +86,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MD5.h"
 #include "llvm/Support/MathExtras.h"
+#include "llvm/Support/SipHash.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Triple.h"
 #include <algorithm>
@@ -3088,6 +3089,17 @@ QualType ASTContext::removeAddrSpaceQualType(QualType T) const {
     return QualType(TypeNode, Quals.getFastQualifiers());
 }
 
+uint16_t ASTContext::getPointerAuthVTablePointerDiscriminator(
+    const CXXRecordDecl *record) {
+  assert(record->isPolymorphic() &&
+         "Attempted to get vtable pointer discriminator on a monomorphic type");
+  std::unique_ptr<MangleContext> MC(createMangleContext());
+  SmallString<256> Str;
+  llvm::raw_svector_ostream Out(Str);
+  MC->mangleCXXVTable(record, Out);
+  return llvm::getPointerAuthStableSipHash16(Str.c_str());
+}
+
 QualType ASTContext::getObjCGCQualType(QualType T,
                                        Qualifiers::GC GCAttr) const {
   QualType CanT = getCanonicalType(T);
@@ -13812,3 +13824,77 @@ StringRef ASTContext::getCUIDHash() const {
   CUIDHash = llvm::utohexstr(llvm::MD5Hash(LangOpts.CUID), /*LowerCase=*/true);
   return CUIDHash;
 }
+
+const CXXRecordDecl *
+ASTContext::baseForVTableAuthentication(const CXXRecordDecl *thisClass) {
+  assert(thisClass);
+  assert(thisClass->isPolymorphic());
+  const CXXRecordDecl *primaryBase = thisClass;
+  while (1) {
+    assert(primaryBase);
+    assert(primaryBase->isPolymorphic());
+    auto &layout = getASTRecordLayout(primaryBase);
+    auto base = layout.getPrimaryBase();
+    if (!base || base == primaryBase || !base->isPolymorphic())
+      break;
+    primaryBase = base;
+  }
+  return primaryBase;
+}
+
+bool ASTContext::useAbbreviatedThunkName(GlobalDecl virtualMethodDecl,
+                                         StringRef mangledName) {
+  auto method = cast<CXXMethodDecl>(virtualMethodDecl.getDecl());
+  assert(method->isVirtual());
+  bool defaultIncludesPointerAuth =
+      LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics;
+
+  if (!defaultIncludesPointerAuth)
+    return true;
+
+  auto existing = thunksToBeAbbreviated.find(virtualMethodDecl);
+  if (existing != thunksToBeAbbreviated.end())
+    return existing->second.contains(mangledName.str());
+
+  std::unique_ptr<MangleContext> mangler(createMangleContext());
+  llvm::StringMap<llvm::SmallVector<std::string, 2>> thunks;
+  auto vtableContext = getVTableContext();
+  if (const auto *thunkInfos = vtableContext->getThunkInfo(virtualMethodDecl)) {
+    auto destructor = dyn_cast<CXXDestructorDecl>(method);
+    for (const auto &thunk : *thunkInfos) {
+      SmallString<256> elidedName;
+      llvm::raw_svector_ostream elidedNameStream(elidedName);
+      if (destructor) {
+        mangler->mangleCXXDtorThunk(destructor, virtualMethodDecl.getDtorType(),
+                                    thunk, /* elideOverrideInfo */ true,
+                                    elidedNameStream);
+      } else {
+        mangler->mangleThunk(method, thunk, /* elideOverrideInfo */ true,
+                             elidedNameStream);
+      }
+      SmallString<256> mangledName;
+      llvm::raw_svector_ostream mangledNameStream(mangledName);
+      if (destructor) {
+        mangler->mangleCXXDtorThunk(destructor, virtualMethodDecl.getDtorType(),
+                                    thunk, /* elideOverrideInfo */ false,
+                                    mangledNameStream);
+      } else {
+        mangler->mangleThunk(method, thunk, /* elideOverrideInfo */ false,
+                             mangledNameStream);
+      }
+
+      if (thunks.find(elidedName) == thunks.end()) {
+        thunks[elidedName] = {};
+      }
+      thunks[elidedName].push_back(std::string(mangledName));
+    }
+  }
+  llvm::StringSet<> simplifiedThunkNames;
+  for (auto &thunkList : thunks) {
+    llvm::sort(thunkList.second);
+    simplifiedThunkNames.insert(thunkList.second[0]);
+  }
+  bool result = simplifiedThunkNames.contains(mangledName);
+  thunksToBeAbbreviated[virtualMethodDecl] = std::move(simplifiedThunkNames);
+  return result;
+}
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index ed9e6eeb36c75..3a8704f366be5 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -99,11 +99,10 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
   }
 
   void mangleCXXName(GlobalDecl GD, raw_ostream &) override;
-  void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
+  void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, bool,
                    raw_ostream &) override;
   void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type,
-                          const ThisAdjustment &ThisAdjustment,
-                          raw_ostream &) override;
+                          const ThunkInfo &Thunk, bool, raw_ostream &) override;
   void mangleReferenceTemporary(const VarDecl *D, unsigned ManglingNumber,
                                 raw_ostream &) override;
   void mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &) override;
@@ -468,6 +467,7 @@ class CXXNameMangler {
   void mangleNameOrStandardSubstitution(const NamedDecl *ND);
   void mangleLambdaSig(const CXXRecordDecl *Lambda);
   void mangleModuleNamePrefix(StringRef Name, bool IsPartition = false);
+  void mangleVendorQualifier(StringRef qualifier);
 
 private:
 
@@ -559,7 +559,6 @@ class CXXNameMangler {
                                       StringRef Prefix = "");
   void mangleOperatorName(DeclarationName Name, unsigned Arity);
   void mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity);
-  void mangleVendorQualifier(StringRef qualifier);
   void mangleQualifiers(Qualifiers Quals, const DependentAddressSpaceType *DAST = nullptr);
   void mangleRefQualifier(RefQualifierKind RefQualifier);
 
@@ -7037,8 +7036,64 @@ void ItaniumMangleContextImpl::mangleCXXDtorComdat(const CXXDestructorDecl *D,
   Mangler.mangle(GlobalDecl(D, Dtor_Comdat));
 }
 
+static void mangleOverrideDiscrimination(CXXNameMangler &mangler,
+                                         ASTContext &context,
+                                         const ThunkInfo &thunk) {
+  auto &langOpts = context.getLangOpts();
+  auto thisType = thunk.ThisType;
+  auto thisRecord = thisType->getPointeeCXXRecordDecl();
+  auto ptrauthClassRecord = context.baseForVTableAuthentication(thisRecord);
+  unsigned typedDiscriminator =
+      context.getPointerAuthVTablePointerDiscriminator(thisRecord);
+  mangler.mangleVendorQualifier("__vtptrauth");
+  auto &manglerStream = mangler.getStream();
+  manglerStream << "I";
+  if (auto explicitAuth =
+          ptrauthClassRecord->getAttr<VTablePointerAuthenticationAttr>()) {
+    manglerStream << "Lj" << explicitAuth->getKey();
+
+    if (explicitAuth->getAddressDiscrimination() ==
+        VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) {
+      manglerStream << "Lb" << langOpts.PointerAuthVTPtrAddressDiscrimination;
+    } else {
+      manglerStream << "Lb"
+                    << (explicitAuth->getAddressDiscrimination() ==
+                        VTablePointerAuthenticationAttr::AddressDiscrimination);
+    }
+
+    switch (explicitAuth->getExtraDiscrimination()) {
+    case VTablePointerAuthenticationAttr::DefaultExtraDiscrimination: {
+      if (langOpts.PointerAuthVTPtrTypeDiscrimination)
+        manglerStream << "Lj" << typedDiscriminator;
+      else
+        manglerStream << "Lj" << 0;
+      break;
+    }
+    case VTablePointerAuthenticationAttr::TypeDiscrimination:
+      manglerStream << "Lj" << typedDiscriminator;
+      break;
+    case VTablePointerAuthenticationAttr::CustomDiscrimination:
+      manglerStream << "Lj" << explicitAuth->getCustomDiscriminationValue();
+      break;
+    case VTablePointerAuthenticationAttr::NoExtraDiscrimination:
+      manglerStream << "Lj" << 0;
+      break;
+    }
+  } else {
+    manglerStream << "Lj"
+                  << (unsigned)VTablePointerAuthenticationAttr::DefaultKey;
+    manglerStream << "Lb" << langOpts.PointerAuthVTPtrAddressDiscrimination;
+    if (langOpts.PointerAuthVTPtrTypeDiscrimination)
+      manglerStream << "Lj" << typedDiscriminator;
+    else
+      manglerStream << "Lj" << 0;
+  }
+  manglerStream << "E";
+}
+
 void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
                                            const ThunkInfo &Thunk,
+                                           bool elideOverrideInfo,
                                            raw_ostream &Out) {
   //  <special-name> ::= T <call-offset> <base encoding>
   //                      # base is the nominal target function of thunk
@@ -7064,21 +7119,29 @@ void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
                              Thunk.Return.Virtual.Itanium.VBaseOffsetOffset);
 
   Mangler.mangleFunctionEncoding(MD);
+  if (!elideOverrideInfo) {
+    mangleOverrideDiscrimination(Mangler, getASTContext(), Thunk);
+  }
 }
 
-void ItaniumMangleContextImpl::mangleCXXDtorThunk(
-    const CXXDestructorDecl *DD, CXXDtorType Type,
-    const ThisAdjustment &ThisAdjustment, raw_ostream &Out) {
+void ItaniumMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
+                                                  CXXDtorType Type,
+                                                  const ThunkInfo &Thunk,
+                                                  bool elideOverrideInfo,
+                                                  raw_ostream &Out) {
   //  <special-name> ::= T <call-offset> <base encoding>
   //                      # base is the nominal target function of thunk
   CXXNameMangler Mangler(*this, Out, DD, Type);
   Mangler.getStream() << "_ZT";
 
+  auto &ThisAdjustment = Thunk.This;
   // Mangle the 'this' pointer adjustment.
   Mangler.mangleCallOffset(ThisAdjustment.NonVirtual,
                            ThisAdjustment.Virtual.Itanium.VCallOffsetOffset);
 
   Mangler.mangleFunctionEncoding(GlobalDecl(DD, Type));
+  if (!elideOverrideInfo)
+    mangleOverrideDiscrimination(Mangler, getASTContext(), Thunk);
 }
 
 /// Returns the mangled name for a guard variable for the passed in VarDecl.
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 30cff1ba2e6f3..bd5d7b01a1144 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -514,10 +514,20 @@ class ASTNameGenerator::Implementation {
       }
     } else if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(ND)) {
       Manglings.emplace_back(getName(ND));
-      if (MD->isVirtual())
-        if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD))
-          for (const auto &T : *TIV)
-            Manglings.emplace_back(getMangledThunk(MD, T));
+      if (MD->isVirtual()) {
+        if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) {
+          for (const auto &T : *TIV) {
+            std::string thunkName;
+            std::string contextualizedName =
+                getMangledThunk(MD, T, /* elideOverrideInfo */ false);
+            if (Ctx.useAbbreviatedThunkName(MD, contextualizedName))
+              thunkName = getMangledThunk(MD, T, /* elideOverrideInfo */ true);
+            else
+              thunkName = contextualizedName;
+            Manglings.emplace_back(thunkName);
+          }
+        }
+      }
     }
 
     return Manglings;
@@ -570,11 +580,12 @@ class ASTNameGenerator::Implementation {
     return BOS.str();
   }
 
-  std::string getMangledThunk(const CXXMethodDecl *MD, const ThunkInfo &T) {
+  std::string getMangledThunk(const CXXMethodDecl *MD, const ThunkInfo &T,
+                              bool elideOverrideInfo) {
     std::string FrontendBuf;
     llvm::raw_string_ostream FOS(FrontendBuf);
 
-    MC->mangleThunk(MD, T, FOS);
+    MC->mangleThunk(MD, T, elideOverrideInfo, FOS);
 
     std::string BackendBuf;
     llvm::raw_string_ostream BOS(BackendBuf);
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 36d611750ca48..c358721cc9b63 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -158,17 +158,18 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext {
   void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD,
                                 const MethodVFTableLocation &ML,
                                 raw_ostream &Out) override;
-  void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
+  void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, bool,
                    raw_ostream &) override;
   void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type,
-                          const ThisAdjustment &ThisAdjustment,
-                          raw_ostream &) override;
+                          const ThunkInfo &Thunk, bool, raw_ostream &) override;
   void mangleCXXVFTable(const CXXRecordDecl *Derived,
                         ArrayRef<const CXXRecordDecl *> BasePath,
                         raw_ostream &Out) override;
   void mangleCXXVBTable(const CXXRecordDecl *Derived,
                         ArrayRef<const CXXRecordDecl *> BasePath,
                         raw_ostream &Out) override;
+
+  void mangleCXXVTable(const CXXRecordDecl *, raw_ostream &) override;
   void mangleCXXVirtualDisplacementMap(const CXXRecordDecl *SrcRD,
                                        const CXXRecordDecl *DstRD,
                                        raw_ostream &Out) override;
@@ -3652,7 +3653,7 @@ void MicrosoftMangleContextImpl::mangleVirtualMemPtrThunk(
 }
 
 void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
-                                             const ThunkInfo &Thunk,
+                                             const ThunkInfo &Thunk, bool,
                                              raw_ostream &Out) {
   msvc_hashing_ostream MHO(Out);
   MicrosoftCXXNameMangler Mangler(*this, MHO);
@@ -3674,9 +3675,10 @@ void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
       DeclForFPT->getType()->castAs<FunctionProtoType>(), MD);
 }
 
-void MicrosoftMangleContextImpl::mangleCXXDtorThunk(
-    const CXXDestructorDecl *DD, CXXDtorType Type,
-    const ThisAdjustment &Adjustment, raw_ostream &Out) {
+void MicrosoftMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
+                                                    CXXDtorType Type,
+                                                    const ThunkInfo &Thunk,
+                                                    bool, raw_ostream &Out) {
   // FIXME: Actually, the dtor thunk should be emitted for vector deleting
   // dtors rather than scalar deleting dtors. Just use the vector deleting dtor
   // mangling manually until we support both deleting dtor types.
@@ -3685,6 +3687,7 @@ void MicrosoftMangleContextImpl::mangleCXXDtorThunk(
   MicrosoftCXXNameMangler Mangler(*this, MHO, DD, Type);
   Mangler.getStream() << "??_E";
   Mangler.mangleName(DD->getParent());
+  auto &Adjustment = Thunk.This;
   mangleThunkThisAdjustment(DD->getAccess(), Adjustment, Mangler, MHO);
   Mangler.mangleFunctionType(DD->getType()->castAs<FunctionProtoType>(), DD);
 }
@@ -3709,6 +3712,12 @@ void MicrosoftMangleContextImpl::mangleCXXVFTable(
   Mangler.getStream() << '@';
 }
 
+void MicrosoftMangleContextImpl::mangleCXXVTable(const CXXRecordDecl *Derived,
+                                                 raw_ostream &Out) {
+  // TODO: Determine appropriate mangling for MSABI
+  mangleCXXVFTable(Derived, {}, Out);
+}
+
 void MicrosoftMangleContextImpl::mangleCXXVBTable(
     const CXXRecordDecl *Derived, ArrayRef<const CXXRecordDecl *> BasePath,
     raw_ostream &Out) {
diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp
index 7ec04d1d20546..c54ba182ed722 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -1169,8 +1169,11 @@ void ItaniumVTableBuilder::ComputeThisAdjustments() {
       //
       // Do not set ThunkInfo::Method if Idx is already in VTableThunks. This
       // can happen when covariant return adjustment is required too.
-      if (!VTableThunks.count(Idx))
-        VTableThunks[Idx].Method = VTables.findOriginalMethodInMap(MD);
+      if (!VTableThunks.count(Idx)) {
+        auto method = VTables.findOriginalMethodInMap(MD);
+        VTableThunks[Idx].Method = method;
+        VTableThunks[Idx].ThisType = method->getThisType().getTypePtr();
+      }
       VTableThunks[Idx].This = ThisAdjustment;
     };
 
@@ -1575,8 +1578,9 @@ void ItaniumVTableBuilder::AddMethods(
               ComputeReturnAdjustment(ReturnAdjustmentOffset);
 
             // This is a virtual thunk for the most derived class, add it.
-            AddThunk(Overrider.Method,
-                     ThunkInfo(ThisAdjustment, ReturnAdjustment));
+            AddThunk(
+                Overrider.Method,
+                ThunkInfo(ThisAdjustment, ReturnAdjustment, OverriddenMD->getThisType().getTypePtr()));
           }
         }
 
@@ -1648,8 +1652,10 @@ void ItaniumVTableBuilder::AddMethods(
     // vtable entry. We need to record the method because we cannot call
     // findOriginalMethod to find the method that created the entry if the
     // method in the entry requires adjustment.
-    if (!ReturnAdjustment.isEmpty())
+    if (!ReturnAdjustment.isEmpty()) {
       VTableThunks[Components.size()].Method = MD;
+      VTableThunks[Components.size()].ThisType = MD->getThisType().getTypePtr();
+    }
 
     AddMethod(Overrider.Method, ReturnAdjustment);
   }
@@ -3182,9 +3188,10 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
                                     ReturnAdjustmentOffset.VirtualBase);
       }
     }
-
+    auto thisType = (OverriddenMD ? OverriddenMD : MD)->getThisType().getTypePtr();
     AddMethod(FinalOverriderMD,
               ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
+                        thisType,
                         ForceReturnAdjustmentMangling ? MD : nullptr));
   }
 }
diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp
index e95a735f92f74..77599cf20d82b 100644
--- a/clang/lib/CodeGen/CGCXX.cpp
+++ b/clang/lib/CodeGen/CGCXX.cpp
@@ -263,7 +263,16 @@ static CGCallee BuildAppleKextVirtualCall(CodeGenFunction &CGF,
     CGF.Builder.CreateConstInBoundsGEP1_64(Ty, VTable, VTableIndex, "vfnkxt");
   llvm::Value *VFunc = CGF.Builder.CreateAlignedLoad(
       Ty, VFuncPtr, llvm::Align(CGF.PointerAlignInBytes));
-  CGCallee Callee(GD, VFunc);
+
+  CGPointerAuthInfo PointerAuth;
+  if (auto &Schema =
+          CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) {
+    auto OrigMD =
+      CGM.getItaniumVTableContext().findOriginalMethod(GD.getCanonicalDecl());
+    PointerAuth = CGF.EmitPointerAuthInfo(Schema, VFuncPtr, OrigMD, QualType());
+  }
+
+  CGCallee Callee(GD, VFunc, PointerAuth);
   return Callee;
 }
 
diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h
index c7eccbd0095a9..fae5d7e2697d1 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -505,13 +505,15 @@ class CGCXXABI {
   virtual void setThunkLinkage(llvm::Function *Thunk, bool ForVTable,
                                GlobalDecl GD, bool ReturnAdjustment) = 0;
 
-  virtual llvm::Value *performThisAdjustment(CodeGenFunction &CGF,
-                                             Address This,
-                                             const ThisAdjustment &TA) = 0;
+  virtual llvm::Value *
+  performThisAdjustment(CodeGenFunction &CGF, Address This,
+                        const CXXRecordDecl *UnadjustedClass,
+                        const ThunkInfo &TI) = 0;
 
-  virtual llvm::Value *performReturnAdjustment(CodeGenFunction &CGF,
-                                               Address Ret,
-                                               const ReturnAdjustment &RA) = 0;
+  virtual llvm::Value *
+  performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
+                          const CXXRecordDecl *UnadjustedClass,
+                          const ReturnAdjustment &RA) = 0;
 
   virtual void EmitReturnFromThunk(CodeGenFunction &CGF,
                                    RValue RV, QualType ResultType);
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index b8cb78266130c..19dca63c9c46f 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -2588,6 +2588,12 @@ void CodeGenFunction::InitializeVTablePointer(const VPtr &Vptr) {
   // the same addr space. Note that this might not be LLVM address space 0.
   VTableField = VTableField.withElementType(PtrTy);
 
+  if (auto authenticationInfo = CGM.getVTablePointerAuthInfo(
+          this, Vptr.Base.getBase(), VTableField.emitRawPointer(*this))) {
+    VTableAddressPoint =
+        EmitPointerAuthSign(*authenticationInfo, VTableAddressPoint);
+  }
+
   llvm::StoreInst *Store = Builder.CreateStore(VTableAddressPoint, VTableField);
   TBAAAccessInfo TBAAInfo = CGM.getTBAAVTablePtrAccessInfo(PtrTy);
   CGM.DecorateInstructionWithTBAA(Store, TBAAInfo);
@@ -2681,12 +2687,35 @@ void CodeGenFunction::InitializeVTablePointers(const CXXRecordDecl *RD) {
 
 llvm::Value *CodeGenFunction::GetVTablePtr(Address This,
                                            llvm::Type *VTableTy,
-                                           const CXXRecordDecl *RD) {
+                                           const CXXRecordDecl *RD,
+                                           VTableAuthMode authMode) {
   Address VTablePtrSrc = This.withElementType(VTableTy);
   llvm::Instruction *VTable = Builder.CreateLoad(VTablePtrSrc, "vtable");
   TBAAAccessInfo TBAAInfo = CGM.getTBAAVTablePtrAccessInfo(VTableTy);
   CGM.DecorateInstructionWithTBAA(VTable, TBAAInfo);
 
+  if (auto authenticationInfo =
+            CGM.getVTablePointerAuthInfo(this, RD, This.emitRawPointer(*this))) {
+    if (authMode != VTableAuthMode::UnsafeUbsanStrip) {
+      VTable = cast<llvm::Instruction>(
+          EmitPointerAuthAuth(*authenticationInfo, VTable));
+      if (authMode == VTableAuthMode::MustTrap) {
+        // This is clearly suboptimal but until we have an ability
+        // to rely on the authentication intrinsic trapping and force
+        // an authentication to occur we don't really have a choice.
+        VTable =
+            cast<llvm::Instruction>(Builder.CreateBitCast(VTable, Int8PtrTy));
+        Builder.CreateLoad(RawAddress(VTable, Int8Ty, CGM.getPointerAlign()),
+                           /* IsVolatile */ true);
+      }
+    } else {
+      VTable = cast<llvm::Instruction>(EmitPointerAuthAuth(
+          CGPointerAuthInfo(0, PointerAuthenticationMode::Strip, false, false,
+                            nullptr),
+          VTable));
+    }
+  }
+
   if (CGM.getCodeGenOpts().OptimizationLevel > 0 &&
       CGM.getCodeGenOpts().StrictVTablePointers)
     CGM.DecorateInstructionWithInvariantGroup(VTable, RD);
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 152f51f5865e0..4907f3b13ac95 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -838,9 +838,12 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,
 
       // Load the vptr, and compute hash_16_bytes(TypeHash, vptr).
       llvm::Value *Low = llvm::ConstantInt::get(Int64Ty, TypeHash);
+      llvm::Type *VPtrTy = llvm::PointerType::get(IntPtrTy, 0);
       Address VPtrAddr(Ptr, IntPtrTy, getPointerAlign());
-      llvm::Value *VPtrVal = Builder.CreateLoad(VPtrAddr);
-      llvm::Value *High = Builder.CreateZExt(VPtrVal, Int64Ty);
+      llvm::Value *VPtrVal = GetVTablePtr(VPtrAddr, VPtrTy,
+                                          Ty->getAsCXXRecordDecl(),
+                                          VTableAuthMode::UnsafeUbsanStrip);
+      llvm::Value *High = Builder.CreateBitOrPointerCast(VPtrVal, Int64Ty);
 
       llvm::Value *Hash = emitHash16Bytes(Builder, Low, High);
       Hash = Builder.CreateTrunc(Hash, IntPtrTy);
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index bfb545a2fe1f6..bed91cfa84ff1 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -803,6 +803,14 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
       llvm::Constant *VTableAddressPoint =
           CGM.getCXXABI().getVTableAddressPoint(BaseSubobject(CD, Offset),
                                                 VTableClass);
+      if (auto authentication =
+              CGM.getVTablePointerAuthentication(VTableClass)) {
+        VTableAddressPoint = Emitter.tryEmitConstantSignedPointer(
+            VTableAddressPoint, *authentication);
+        if (!VTableAddressPoint) {
+          return false;
+        }
+      }
       if (!AppendBytes(Offset, VTableAddressPoint))
         return false;
     }
@@ -1520,8 +1528,37 @@ llvm::GlobalValue *ConstantEmitter::getCurrentAddrPrivate() {
   return global;
 }
 
+static llvm::Constant *getUnfoldableValue(llvm::Constant *C) {
+  // Look through any constant expressions that might get folded
+  while (auto CE = dyn_cast<llvm::ConstantExpr>(C)) {
+    switch (CE->getOpcode()) {
+    // Simple type changes.
+    case llvm::Instruction::BitCast:
+    case llvm::Instruction::IntToPtr:
+    case llvm::Instruction::PtrToInt:
+      break;
+
+    // GEPs, if all the indices are zero.
+    case llvm::Instruction::GetElementPtr:
+      for (unsigned i = 1, e = CE->getNumOperands(); i != e; ++i)
+        if (!CE->getOperand(i)->isNullValue())
+          return C;
+      break;
+
+    default:
+      return C;
+    }
+    C = CE->getOperand(0);
+  }
+  return C;
+}
+
 void ConstantEmitter::registerCurrentAddrPrivate(llvm::Constant *signal,
                                            llvm::GlobalValue *placeholder) {
+  // Strip anything from the signal value that might get folded into other
+  // constant expressions in the final initializer.
+  signal = getUnfoldableValue(signal);
+
   assert(!PlaceholderAddresses.empty());
   assert(PlaceholderAddresses.back().first == nullptr);
   assert(PlaceholderAddresses.back().second == placeholder);
@@ -1579,7 +1616,7 @@ namespace {
       // messing around with llvm::Constant structures, which never itself
       // does anything that should be visible in compiler output.
       for (auto &entry : Locations) {
-        assert(entry.first->getParent() == nullptr && "not a placeholder!");
+        assert(entry.first->getName() == "" && "not a placeholder!");
         entry.first->replaceAllUsesWith(entry.second);
         entry.first->eraseFromParent();
       }
@@ -1743,6 +1780,44 @@ llvm::Constant *ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
   return (C ? emitForMemory(C, destType) : nullptr);
 }
 
+/// Try to emit a constant signed pointer, given a raw pointer and the
+/// destination ptrauth qualifier.
+///
+/// This can fail if the qualifier needs address discrimination and the
+/// emitter is in an abstract mode.
+llvm::Constant *
+ConstantEmitter::tryEmitConstantSignedPointer(llvm::Constant *unsignedPointer,
+                                              PointerAuthQualifier schema) {
+  assert(schema && "applying trivial ptrauth schema");
+
+  if (schema.hasKeyNone())
+    return unsignedPointer;
+
+  auto key = schema.getKey();
+
+  // Create an address placeholder if we're using address discrimination.
+  llvm::GlobalValue *storageAddress = nullptr;
+  if (schema.isAddressDiscriminated()) {
+    // We can't do this if the emitter is in an abstract state.
+    if (isAbstract())
+      return nullptr;
+
+    storageAddress = getCurrentAddrPrivate();
+  }
+
+  // Fetch the extra discriminator.
+  llvm::Constant *otherDiscriminator =
+      llvm::ConstantInt::get(CGM.IntPtrTy, schema.getExtraDiscriminator());
+
+  auto signedPointer = CGM.getConstantSignedPointer(
+      unsignedPointer, key, storageAddress, otherDiscriminator);
+
+  if (schema.isAddressDiscriminated())
+    registerCurrentAddrPrivate(signedPointer, storageAddress);
+
+  return signedPointer;
+}
+
 llvm::Constant *ConstantEmitter::emitForMemory(CodeGenModule &CGM,
                                                llvm::Constant *C,
                                                QualType destType) {
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp
index adfa721ac89d3..e0c5ff101d858 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -23,11 +23,53 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/ValueMap.h"
 #include "llvm/Analysis/ValueTracking.h"
+#include "llvm/Support/SipHash.h"
 #include <vector>
 
 using namespace clang;
 using namespace CodeGen;
 
+/// Given a pointer-authentication schema, return a concrete "other"
+/// discriminator for it.
+llvm::Constant *CodeGenModule::getPointerAuthOtherDiscriminator(
+    const PointerAuthSchema &schema, GlobalDecl decl, QualType type) {
+  switch (schema.getOtherDiscrimination()) {
+  case PointerAuthSchema::Discrimination::None:
+    return nullptr;
+
+  case PointerAuthSchema::Discrimination::Type:
+    llvm_unreachable("type discrimination not implemented yet");
+
+  case PointerAuthSchema::Discrimination::Decl:
+    assert(decl.getDecl() &&
+           "declaration not provided for decl-discriminated schema");
+    return llvm::ConstantInt::get(IntPtrTy,
+                                  getPointerAuthDeclDiscriminator(decl));
+
+  case PointerAuthSchema::Discrimination::Constant:
+    return llvm::ConstantInt::get(IntPtrTy, schema.getConstantDiscrimination());
+  }
+  llvm_unreachable("bad discrimination kind");
+}
+
+uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM,
+                                                  GlobalDecl declaration) {
+  return CGM.getPointerAuthDeclDiscriminator(declaration);
+}
+
+/// Return the "other" decl-specific discriminator for the given decl.
+uint16_t
+CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl declaration) {
+  uint16_t &entityHash = PtrAuthDiscriminatorHashes[declaration];
+
+  if (entityHash == 0) {
+    StringRef name = getMangledName(declaration);
+    entityHash = llvm::getPointerAuthStableSipHash16(name);
+  }
+
+  return entityHash;
+}
+
 /// Return the abstract pointer authentication schema for a pointer to the given
 /// function type.
 CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) {
@@ -46,6 +88,41 @@ CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) {
                            /*Discriminator=*/nullptr);
 }
 
+llvm::Value *
+CodeGenFunction::EmitPointerAuthBlendDiscriminator(llvm::Value *storageAddress,
+                                                   llvm::Value *discriminator) {
+  storageAddress = Builder.CreatePtrToInt(storageAddress, IntPtrTy);
+  auto intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_blend);
+  return Builder.CreateCall(intrinsic, {storageAddress, discriminator});
+}
+
+/// Emit the concrete pointer authentication informaton for the
+/// given authentication schema.
+CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo(
+    const PointerAuthSchema &schema, llvm::Value *storageAddress,
+    GlobalDecl schemaDecl, QualType schemaType) {
+  if (!schema)
+    return CGPointerAuthInfo();
+
+  llvm::Value *discriminator =
+      CGM.getPointerAuthOtherDiscriminator(schema, schemaDecl, schemaType);
+
+  if (schema.isAddressDiscriminated()) {
+    assert(storageAddress &&
+           "address not provided for address-discriminated schema");
+
+    if (discriminator)
+      discriminator =
+          EmitPointerAuthBlendDiscriminator(storageAddress, discriminator);
+    else
+      discriminator = Builder.CreatePtrToInt(storageAddress, IntPtrTy);
+  }
+
+  return CGPointerAuthInfo(schema.getKey(), schema.getAuthenticationMode(),
+                           schema.isIsaPointer(),
+                           schema.authenticatesNullValues(), discriminator);
+}
+
 /// Build a signed-pointer "ptrauth" constant.
 static llvm::ConstantPtrAuth *
 buildConstantAddress(CodeGenModule &CGM, llvm::Constant *pointer, unsigned key,
@@ -85,6 +162,27 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *pointer,
                               otherDiscriminator);
 }
 
+/// Does a given PointerAuthScheme require us to sign a value
+static bool shouldSignPointer(const PointerAuthSchema &schema) {
+  auto authenticationMode = schema.getAuthenticationMode();
+  return authenticationMode == PointerAuthenticationMode::SignAndStrip ||
+         authenticationMode == PointerAuthenticationMode::SignAndAuth;
+}
+
+/// Sign a constant pointer using the given scheme, producing a constant
+/// with the same IR type.
+llvm::Constant *CodeGenModule::getConstantSignedPointer(
+    llvm::Constant *pointer, const PointerAuthSchema &schema,
+    llvm::Constant *storageAddress, GlobalDecl schemaDecl,
+    QualType schemaType) {
+  assert(shouldSignPointer(schema));
+  llvm::Constant *otherDiscriminator =
+      getPointerAuthOtherDiscriminator(schema, schemaDecl, schemaType);
+
+  return getConstantSignedPointer(pointer, schema.getKey(), storageAddress,
+                                  otherDiscriminator);
+}
+
 llvm::Constant *
 CodeGen::getConstantSignedPointer(CodeGenModule &CGM,
                                   llvm::Constant *pointer, unsigned key,
@@ -94,6 +192,37 @@ CodeGen::getConstantSignedPointer(CodeGenModule &CGM,
                                       otherDiscriminator);
 }
 
+/// Sign the given pointer and add it to the constant initializer
+/// currently being built.
+void ConstantAggregateBuilderBase::addSignedPointer(
+    llvm::Constant *pointer, const PointerAuthSchema &schema,
+    GlobalDecl calleeDecl, QualType calleeType) {
+  if (!schema || !shouldSignPointer(schema))
+    return add(pointer);
+
+  llvm::Constant *storageAddress = nullptr;
+  if (schema.isAddressDiscriminated()) {
+    storageAddress = getAddrOfCurrentPosition(pointer->getType());
+  }
+
+  llvm::Constant *signedPointer = Builder.CGM.getConstantSignedPointer(
+      pointer, schema, storageAddress, calleeDecl, calleeType);
+  add(signedPointer);
+}
+
+void ConstantAggregateBuilderBase::addSignedPointer(
+    llvm::Constant *pointer, unsigned key, bool useAddressDiscrimination,
+    llvm::Constant *otherDiscriminator) {
+  llvm::Constant *storageAddress = nullptr;
+  if (useAddressDiscrimination) {
+    storageAddress = getAddrOfCurrentPosition(pointer->getType());
+  }
+
+  llvm::Constant *signedPointer = Builder.CGM.getConstantSignedPointer(
+      pointer, key, storageAddress, otherDiscriminator);
+  add(signedPointer);
+}
+
 /// If applicable, sign a given constant function pointer with the ABI rules for
 /// functionType.
 llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *pointer,
@@ -126,3 +255,113 @@ llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD,
 
   return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType, GD);
 }
+
+std::optional<PointerAuthQualifier>
+CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *thisClass) {
+  auto defaultAuthentication = getCodeGenOpts().PointerAuth.CXXVTablePointers;
+  if (!defaultAuthentication)
+    return std::nullopt;
+  const CXXRecordDecl *primaryBase =
+      Context.baseForVTableAuthentication(thisClass);
+
+  unsigned key = defaultAuthentication.getKey();
+  bool addressDiscriminated = defaultAuthentication.isAddressDiscriminated();
+  auto defaultDiscrimination = defaultAuthentication.getOtherDiscrimination();
+  unsigned typeBasedDiscriminator =
+      Context.getPointerAuthVTablePointerDiscriminator(primaryBase);
+  unsigned discriminator;
+  if (defaultDiscrimination == PointerAuthSchema::Discrimination::Type) {
+    discriminator = typeBasedDiscriminator;
+  } else if (defaultDiscrimination ==
+             PointerAuthSchema::Discrimination::Constant) {
+    discriminator = defaultAuthentication.getConstantDiscrimination();
+  } else {
+    assert(defaultDiscrimination == PointerAuthSchema::Discrimination::None);
+    discriminator = 0;
+  }
+  if (auto explicitAuthentication =
+          primaryBase->getAttr<VTablePointerAuthenticationAttr>()) {
+    auto explicitKey = explicitAuthentication->getKey();
+    auto explicitAddressDiscrimination =
+        explicitAuthentication->getAddressDiscrimination();
+    auto explicitDiscriminator =
+        explicitAuthentication->getExtraDiscrimination();
+    if (explicitKey == VTablePointerAuthenticationAttr::NoKey) {
+      return std::nullopt;
+    }
+    if (explicitKey != VTablePointerAuthenticationAttr::DefaultKey) {
+      if (explicitKey == VTablePointerAuthenticationAttr::ProcessIndependent)
+        key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDA;
+      else {
+        assert(explicitKey ==
+               VTablePointerAuthenticationAttr::ProcessDependent);
+        key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDB;
+      }
+    }
+
+    if (explicitAddressDiscrimination !=
+        VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) {
+      addressDiscriminated =
+          explicitAddressDiscrimination ==
+          VTablePointerAuthenticationAttr::AddressDiscrimination;
+    }
+
+    if (explicitDiscriminator ==
+        VTablePointerAuthenticationAttr::TypeDiscrimination) {
+      discriminator = typeBasedDiscriminator;
+    } else if (explicitDiscriminator ==
+               VTablePointerAuthenticationAttr::CustomDiscrimination) {
+      discriminator = explicitAuthentication->getCustomDiscriminationValue();
+    } else if (explicitDiscriminator == VTablePointerAuthenticationAttr::NoExtraDiscrimination) {
+      discriminator = 0;
+    }
+  }
+  return PointerAuthQualifier::Create(key, addressDiscriminated, discriminator,
+                                      PointerAuthenticationMode::SignAndAuth,
+                                      /* isIsaPointer */ false,
+                                      /* authenticatesNullValues */ false);
+}
+
+std::optional<PointerAuthQualifier>
+CodeGenModule::getVTablePointerAuthentication(const CXXRecordDecl *record) {
+  if (!record->getDefinition() || !record->isPolymorphic())
+    return std::nullopt;
+
+  auto existing = VTablePtrAuthInfos.find(record);
+  std::optional<PointerAuthQualifier> authentication;
+  if (existing != VTablePtrAuthInfos.end()) {
+    authentication = existing->getSecond();
+  } else {
+    authentication = computeVTPointerAuthentication(record);
+    VTablePtrAuthInfos.insert(std::make_pair(record, authentication));
+  }
+  return authentication;
+}
+
+std::optional<CGPointerAuthInfo>
+CodeGenModule::getVTablePointerAuthInfo(CodeGenFunction *CGF,
+                                        const CXXRecordDecl *record,
+                                        llvm::Value *storageAddress) {
+  auto authentication = getVTablePointerAuthentication(record);
+  if (!authentication)
+    return std::nullopt;
+
+  llvm::Value *discriminator = nullptr;
+  if (auto extraDiscriminator = authentication->getExtraDiscriminator()) {
+    discriminator = llvm::ConstantInt::get(IntPtrTy, extraDiscriminator);
+  }
+  if (authentication->isAddressDiscriminated()) {
+    assert(storageAddress &&
+           "address not provided for address-discriminated schema");
+    if (discriminator)
+      discriminator =
+          CGF->EmitPointerAuthBlendDiscriminator(storageAddress, discriminator);
+    else
+      discriminator = CGF->Builder.CreatePtrToInt(storageAddress, IntPtrTy);
+  }
+
+  return CGPointerAuthInfo(authentication->getKey(),
+                           PointerAuthenticationMode::SignAndAuth,
+                           /* IsIsaPointer */ false,
+                           /* authenticatesNullValues */ false, discriminator);
+}
diff --git a/clang/lib/CodeGen/CGVTT.cpp b/clang/lib/CodeGen/CGVTT.cpp
index 4cebb750c89e8..6daa8d2b7668c 100644
--- a/clang/lib/CodeGen/CGVTT.cpp
+++ b/clang/lib/CodeGen/CGVTT.cpp
@@ -90,6 +90,11 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT,
      llvm::Constant *Init = llvm::ConstantExpr::getGetElementPtr(
          VTable->getValueType(), VTable, Idxs, /*InBounds=*/true, InRange);
 
+     if (auto &schema =
+             CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers)
+       Init = CGM.getConstantSignedPointer(Init, schema, nullptr, GlobalDecl(),
+                                           QualType());
+
      VTTComponents.push_back(Init);
   }
 
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 8d9c22546b420..b8f27b626450a 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -95,7 +95,7 @@ static RValue PerformReturnAdjustment(CodeGenFunction &CGF,
       CGF,
       Address(ReturnValue, CGF.ConvertTypeForMem(ResultType->getPointeeType()),
               ClassAlign),
-      Thunk.Return);
+      ClassDecl, Thunk.Return);
 
   if (NullCheckValue) {
     CGF.Builder.CreateBr(AdjustEnd);
@@ -219,8 +219,10 @@ CodeGenFunction::GenerateVarArgsThunk(llvm::Function *Fn,
          "Store of this should be in entry block?");
   // Adjust "this", if necessary.
   Builder.SetInsertPoint(&*ThisStore);
-  llvm::Value *AdjustedThisPtr =
-      CGM.getCXXABI().performThisAdjustment(*this, ThisPtr, Thunk.This);
+
+  const CXXRecordDecl *thisValueClass = Thunk.ThisType->getPointeeCXXRecordDecl();
+  llvm::Value *AdjustedThisPtr = CGM.getCXXABI().performThisAdjustment(
+      *this, ThisPtr, thisValueClass, Thunk);
   AdjustedThisPtr = Builder.CreateBitCast(AdjustedThisPtr,
                                           ThisStore->getOperand(0)->getType());
   ThisStore->setOperand(0, AdjustedThisPtr);
@@ -307,10 +309,15 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee,
   const CXXMethodDecl *MD = cast<CXXMethodDecl>(CurGD.getDecl());
 
   // Adjust the 'this' pointer if necessary
+  const CXXRecordDecl *thisValueClass =
+      MD->getThisType()->getPointeeCXXRecordDecl();
+  if (Thunk)
+    thisValueClass = Thunk->ThisType->getPointeeCXXRecordDecl();
+
   llvm::Value *AdjustedThisPtr =
-    Thunk ? CGM.getCXXABI().performThisAdjustment(
-                          *this, LoadCXXThisAddress(), Thunk->This)
-          : LoadCXXThis();
+      Thunk ? CGM.getCXXABI().performThisAdjustment(*this, LoadCXXThisAddress(),
+                                                    thisValueClass, *Thunk)
+            : LoadCXXThis();
 
   // If perfect forwarding is required a variadic method, a method using
   // inalloca, or an unprototyped thunk, use musttail. Emit an error if this
@@ -504,10 +511,22 @@ llvm::Constant *CodeGenVTables::maybeEmitThunk(GlobalDecl GD,
   SmallString<256> Name;
   MangleContext &MCtx = CGM.getCXXABI().getMangleContext();
   llvm::raw_svector_ostream Out(Name);
-  if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD))
-    MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI.This, Out);
-  else
-    MCtx.mangleThunk(MD, TI, Out);
+
+  if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
+    MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI,
+                            /* elideOverrideInfo */ false, Out);
+  } else
+    MCtx.mangleThunk(MD, TI, /* elideOverrideInfo */ false, Out);
+
+  if (CGM.getContext().useAbbreviatedThunkName(GD, Name.str())) {
+    Name = "";
+    if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
+      MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI,
+                              /* elideOverrideInfo */ true, Out);
+    } else
+      MCtx.mangleThunk(MD, TI, /* elideOverrideInfo */ true, Out);
+  }
+
   llvm::Type *ThunkVTableTy = CGM.getTypes().GetFunctionTypeForVTable(GD);
   llvm::Constant *Thunk = CGM.GetAddrOfThunk(Name, ThunkVTableTy, GD);
 
@@ -819,11 +838,17 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
 
       nextVTableThunkIndex++;
       fnPtr = maybeEmitThunk(GD, thunkInfo, /*ForVTable=*/true);
+      if (CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) {
+        assert(thunkInfo.Method &&  "Method not set");
+        GD = GD.getWithDecl(thunkInfo.Method);
+      }
 
     // Otherwise we can use the method definition directly.
     } else {
       llvm::Type *fnTy = CGM.getTypes().GetFunctionTypeForVTable(GD);
       fnPtr = CGM.GetAddrOfFunction(GD, fnTy, /*ForVTable=*/true);
+      if (CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers)
+        GD = getItaniumVTableContext().findOriginalMethod(GD);
     }
 
     if (useRelativeLayout()) {
@@ -841,6 +866,9 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
       if (FnAS != GVAS)
         fnPtr =
             llvm::ConstantExpr::getAddrSpaceCast(fnPtr, CGM.GlobalsInt8PtrTy);
+      if (auto &schema =
+          CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers)
+        return builder.addSignedPointer(fnPtr, schema, GD, QualType());
       return builder.add(fnPtr);
     }
   }
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index cb5acf1a1abb7..a3e82bc2d0bd5 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3099,3 +3099,30 @@ CodeGenFunction::EmitPointerAuthSign(const CGPointerAuthInfo &pointerAuth,
   return EmitPointerAuthCommon(*this, pointerAuth, pointer,
                                llvm::Intrinsic::ptrauth_sign);
 }
+
+static llvm::Value *EmitStrip(CodeGenFunction &CGF,
+                              const CGPointerAuthInfo &pointerAuth,
+                              llvm::Value *pointer) {
+  auto stripIntrinsic = CGF.CGM.getIntrinsic(llvm::Intrinsic::ptrauth_strip);
+
+  auto key = CGF.Builder.getInt32(pointerAuth.getKey());
+  // Convert the pointer to intptr_t before signing it.
+  auto origType = pointer->getType();
+  pointer = CGF.EmitRuntimeCall(
+      stripIntrinsic, {CGF.Builder.CreatePtrToInt(pointer, CGF.IntPtrTy), key});
+  return CGF.Builder.CreateIntToPtr(pointer, origType);
+}
+
+llvm::Value *
+CodeGenFunction::EmitPointerAuthAuth(const CGPointerAuthInfo &pointerAuth,
+                                     llvm::Value *pointer) {
+  if (pointerAuth.shouldStrip()) {
+    return EmitStrip(*this, pointerAuth, pointer);
+  }
+  if (!pointerAuth.shouldAuth()) {
+    return pointer;
+  }
+
+  return EmitPointerAuthCommon(*this, pointerAuth, pointer,
+                               llvm::Intrinsic::ptrauth_auth);
+}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index a0518b7525df1..50e5ca7bd6f17 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -2456,10 +2456,20 @@ class CodeGenFunction : public CodeGenTypeCache {
 
   void InitializeVTablePointers(const CXXRecordDecl *ClassDecl);
 
+  // VTableTrapMode - whether we guarantee that loading the
+  // vtable is guaranteed to trap on authentication failure,
+  // even if the resulting vtable pointer is unused.
+  enum class VTableAuthMode {
+    Authenticate,
+    MustTrap,
+    UnsafeUbsanStrip // Should only be used for Vptr UBSan check
+  };
   /// GetVTablePtr - Return the Value of the vtable pointer member pointed
   /// to by This.
-  llvm::Value *GetVTablePtr(Address This, llvm::Type *VTableTy,
-                            const CXXRecordDecl *VTableClass);
+  llvm::Value *
+  GetVTablePtr(Address This, llvm::Type *VTableTy,
+               const CXXRecordDecl *VTableClass,
+               VTableAuthMode authMode = VTableAuthMode::Authenticate);
 
   enum CFITypeCheckKind {
     CFITCK_VCall,
@@ -4406,6 +4416,10 @@ class CodeGenFunction : public CodeGenTypeCache {
   }
 
   bool isPointerKnownNonNull(const Expr *E);
+
+  /// Create the discriminator from the storage address and the entity hash.
+  llvm::Value *EmitPointerAuthBlendDiscriminator(llvm::Value *storageAddress,
+                                                 llvm::Value *discriminator);
   CGPointerAuthInfo EmitPointerAuthInfo(const PointerAuthSchema &schema,
                                         llvm::Value *storageAddress,
                                         GlobalDecl calleeDecl,
@@ -4413,6 +4427,8 @@ class CodeGenFunction : public CodeGenTypeCache {
   llvm::Value *EmitPointerAuthSign(QualType pointeeType, llvm::Value *pointer);
   llvm::Value *EmitPointerAuthSign(const CGPointerAuthInfo &info,
                                    llvm::Value *pointer);
+  llvm::Value *EmitPointerAuthAuth(const CGPointerAuthInfo &info,
+                                   llvm::Value *pointer);
   void EmitPointerAuthOperandBundle(
       const CGPointerAuthInfo &info,
       SmallVectorImpl<llvm::OperandBundleDef> &bundles);
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 5e30c5740b2f1..e0827bd315902 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -611,6 +611,13 @@ class CodeGenModule : public CodeGenTypeCache {
   std::pair<std::unique_ptr<CodeGenFunction>, const TopLevelStmtDecl *>
       GlobalTopLevelStmtBlockInFlight;
 
+  llvm::DenseMap<GlobalDecl, uint16_t> PtrAuthDiscriminatorHashes;
+
+  llvm::DenseMap<const CXXRecordDecl *, std::optional<PointerAuthQualifier>>
+      VTablePtrAuthInfos;
+  std::optional<PointerAuthQualifier>
+  computeVTPointerAuthentication(const CXXRecordDecl *thisClass);
+
 public:
   CodeGenModule(ASTContext &C, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
                 const HeaderSearchOptions &headersearchopts,
@@ -975,6 +982,18 @@ class CodeGenModule : public CodeGenTypeCache {
                                            llvm::Constant *storageAddress,
                                            llvm::Constant *extraDiscrim);
 
+  llvm::Constant *
+  getPointerAuthOtherDiscriminator(const PointerAuthSchema &schema,
+                                   GlobalDecl schemaDecl, QualType schemaType);
+  uint16_t getPointerAuthDeclDiscriminator(GlobalDecl GD);
+  std::optional<CGPointerAuthInfo>
+  getVTablePointerAuthInfo(CodeGenFunction *context,
+                           const CXXRecordDecl *record,
+                           llvm::Value *storageAddress);
+
+  std::optional<PointerAuthQualifier>
+  getVTablePointerAuthentication(const CXXRecordDecl *thisClass);
+
   CGPointerAuthInfo EmitPointerAuthInfo(const RecordDecl *RD);
 
   // Return whether RTTI information should be emitted for this target.
diff --git a/clang/lib/CodeGen/ConstantEmitter.h b/clang/lib/CodeGen/ConstantEmitter.h
index a55da0dcad792..93f69e18fc979 100644
--- a/clang/lib/CodeGen/ConstantEmitter.h
+++ b/clang/lib/CodeGen/ConstantEmitter.h
@@ -113,6 +113,9 @@ class ConstantEmitter {
   llvm::Constant *tryEmitAbstract(const APValue &value, QualType T);
   llvm::Constant *tryEmitAbstractForMemory(const APValue &value, QualType T);
 
+  llvm::Constant *tryEmitConstantSignedPointer(llvm::Constant *ptr,
+                                               PointerAuthQualifier auth);
+
   llvm::Constant *tryEmitConstantExpr(const ConstantExpr *CE);
 
   llvm::Constant *emitNullForMemory(QualType T) {
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 8427286dee887..792c750c84239 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -23,6 +23,7 @@
 #include "CGVTables.h"
 #include "CodeGenFunction.h"
 #include "CodeGenModule.h"
+#include "ConstantEmitter.h"
 #include "TargetInfo.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Mangle.h"
@@ -336,9 +337,11 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
   bool exportThunk() override { return true; }
 
   llvm::Value *performThisAdjustment(CodeGenFunction &CGF, Address This,
-                                     const ThisAdjustment &TA) override;
+                                     const CXXRecordDecl *UnadjustedThisClass,
+                                     const ThunkInfo &TI) override;
 
   llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
+                                       const CXXRecordDecl *UnadjustedRetClass,
                                        const ReturnAdjustment &RA) override;
 
   size_t getSrcArgforCopyCtor(const CXXConstructorDecl *,
@@ -1478,10 +1481,17 @@ llvm::Value *ItaniumCXXABI::emitDynamicCastCall(
       computeOffsetHint(CGF.getContext(), SrcDecl, DestDecl).getQuantity());
 
   // Emit the call to __dynamic_cast.
-  llvm::Value *Args[] = {ThisAddr.emitRawPointer(CGF), SrcRTTI, DestRTTI,
-                         OffsetHint};
-  llvm::Value *Value =
-      CGF.EmitNounwindRuntimeCall(getItaniumDynamicCastFn(CGF), Args);
+  llvm::Value *Value = ThisAddr.emitRawPointer(CGF);
+  if (CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) {
+    llvm::Value *vtable =
+        CGF.GetVTablePtr(ThisAddr, CGM.Int8PtrTy, SrcDecl,
+                         CodeGenFunction::VTableAuthMode::MustTrap);
+    assert(vtable);
+    (void)vtable;
+  }
+
+  llvm::Value *args[] = {Value, SrcRTTI, DestRTTI, OffsetHint};
+  Value = CGF.EmitNounwindRuntimeCall(getItaniumDynamicCastFn(CGF), args);
 
   /// C++ [expr.dynamic.cast]p9:
   ///   A failed cast to reference type throws std::bad_cast
@@ -1956,8 +1966,18 @@ llvm::Value *ItaniumCXXABI::getVTableAddressPointInStructorWithVTT(
                                                  VirtualPointerIndex);
 
   // And load the address point from the VTT.
-  return CGF.Builder.CreateAlignedLoad(CGF.GlobalsVoidPtrTy, VTT,
-                                       CGF.getPointerAlign());
+  llvm::Value *AP =
+      CGF.Builder.CreateAlignedLoad(CGF.GlobalsVoidPtrTy, VTT,
+                                    CGF.getPointerAlign());
+
+  if (auto &Schema = CGF.CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers) {
+    CGPointerAuthInfo PointerAuth = CGF.EmitPointerAuthInfo(Schema, VTT,
+                                                            GlobalDecl(),
+                                                            QualType());
+    AP = CGF.EmitPointerAuthAuth(PointerAuth, AP);
+  }
+
+  return AP;
 }
 
 llvm::GlobalVariable *ItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *RD,
@@ -2009,8 +2029,9 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
   llvm::Value *VTable = CGF.GetVTablePtr(This, PtrTy, MethodDecl->getParent());
 
   uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
-  llvm::Value *VFunc;
-  if (CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) {
+  llvm::Value *VFunc, *VTableSlotPtr = nullptr;
+  auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers;
+  if (!Schema && CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) {
     VFunc = CGF.EmitVTableTypeCheckedLoad(
         MethodDecl->getParent(), VTable, PtrTy,
         VTableIndex *
@@ -2025,7 +2046,7 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
           CGM.getIntrinsic(llvm::Intrinsic::load_relative, {CGM.Int32Ty}),
           {VTable, llvm::ConstantInt::get(CGM.Int32Ty, 4 * VTableIndex)});
     } else {
-      llvm::Value *VTableSlotPtr = CGF.Builder.CreateConstInBoundsGEP1_64(
+      VTableSlotPtr = CGF.Builder.CreateConstInBoundsGEP1_64(
           PtrTy, VTable, VTableIndex, "vfn");
       VFuncLoad = CGF.Builder.CreateAlignedLoad(PtrTy, VTableSlotPtr,
                                                 CGF.getPointerAlign());
@@ -2049,7 +2070,13 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
     VFunc = VFuncLoad;
   }
 
-  CGCallee Callee(GD, VFunc);
+  CGPointerAuthInfo PointerAuth;
+  if (Schema) {
+    assert(VTableSlotPtr && "virtual function pointer not set");
+    GD = CGM.getItaniumVTableContext().findOriginalMethod(GD.getCanonicalDecl());
+    PointerAuth = CGF.EmitPointerAuthInfo(Schema, VTableSlotPtr, GD, QualType());
+  }
+  CGCallee Callee(GD, VFunc, PointerAuth);
   return Callee;
 }
 
@@ -2145,6 +2172,7 @@ bool ItaniumCXXABI::canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const {
 }
 static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
                                           Address InitialPtr,
+                                          const CXXRecordDecl *UnadjustedClass,
                                           int64_t NonVirtualAdjustment,
                                           int64_t VirtualAdjustment,
                                           bool IsReturnAdjustment) {
@@ -2162,8 +2190,8 @@ static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
   // Perform the virtual adjustment if we have one.
   llvm::Value *ResultPtr;
   if (VirtualAdjustment) {
-    Address VTablePtrPtr = V.withElementType(CGF.Int8PtrTy);
-    llvm::Value *VTablePtr = CGF.Builder.CreateLoad(VTablePtrPtr);
+    llvm::Value *VTablePtr =
+        CGF.GetVTablePtr(V, CGF.Int8PtrTy, UnadjustedClass);
 
     llvm::Value *Offset;
     llvm::Value *OffsetPtr = CGF.Builder.CreateConstInBoundsGEP1_64(
@@ -2198,18 +2226,20 @@ static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
   return ResultPtr;
 }
 
-llvm::Value *ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF,
-                                                  Address This,
-                                                  const ThisAdjustment &TA) {
-  return performTypeAdjustment(CGF, This, TA.NonVirtual,
-                               TA.Virtual.Itanium.VCallOffsetOffset,
+llvm::Value *
+ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF, Address This,
+                                     const CXXRecordDecl *UnadjustedClass,
+                                     const ThunkInfo &TI) {
+  return performTypeAdjustment(CGF, This, UnadjustedClass, TI.This.NonVirtual,
+                               TI.This.Virtual.Itanium.VCallOffsetOffset,
                                /*IsReturnAdjustment=*/false);
 }
 
 llvm::Value *
 ItaniumCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
+                                       const CXXRecordDecl *UnadjustedClass,
                                        const ReturnAdjustment &RA) {
-  return performTypeAdjustment(CGF, Ret, RA.NonVirtual,
+  return performTypeAdjustment(CGF, Ret, UnadjustedClass, RA.NonVirtual,
                                RA.Virtual.Itanium.VBaseOffsetOffset,
                                /*IsReturnAdjustment=*/true);
 }
@@ -3690,6 +3720,10 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty) {
                                                           VTable, Two);
   }
 
+  if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXTypeInfoVTablePointer)
+    VTable = CGM.getConstantSignedPointer(VTable, Schema, nullptr, GlobalDecl(),
+                                          QualType(Ty, 0));
+
   Fields.push_back(VTable);
 }
 
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index e4f798f6a97d9..a8e65e2cd4baf 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -415,9 +415,11 @@ class MicrosoftCXXABI : public CGCXXABI {
   bool exportThunk() override { return false; }
 
   llvm::Value *performThisAdjustment(CodeGenFunction &CGF, Address This,
-                                     const ThisAdjustment &TA) override;
+                                     const CXXRecordDecl *UnadjustedClass,
+                                     const ThunkInfo &TI) override;
 
   llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
+                                       const CXXRecordDecl *UnadjustedClass,
                                        const ReturnAdjustment &RA) override;
 
   void EmitThreadLocalInitFuncs(
@@ -2227,7 +2229,9 @@ void MicrosoftCXXABI::emitVBTableDefinition(const VPtrInfo &VBT,
 
 llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,
                                                     Address This,
-                                                    const ThisAdjustment &TA) {
+                                                    const CXXRecordDecl *,
+                                                    const ThunkInfo &TI) {
+  auto &TA = TI.This;
   if (TA.isEmpty())
     return This.emitRawPointer(CGF);
 
@@ -2279,6 +2283,7 @@ llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,
 
 llvm::Value *
 MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
+                                         const CXXRecordDecl *,
                                          const ReturnAdjustment &RA) {
   if (RA.isEmpty())
     return Ret.emitRawPointer(CGF);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index b4ca3e413b2c0..205d89118d257 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1468,6 +1468,18 @@ bool CompilerInvocation::setDefaultPointerAuthOptions(
       // If you change anything here, be sure to update <ptrauth.h>.
       Opts.FunctionPointers =
           PointerAuthSchema(Key::ASIA, false, Discrimination::None);
+
+      Opts.CXXVTablePointers = PointerAuthSchema(
+          Key::ASDA, LangOpts.PointerAuthVTPtrAddressDiscrimination,
+          LangOpts.PointerAuthVTPtrTypeDiscrimination ? Discrimination::Type
+                                                      : Discrimination::None);
+      Opts.CXXTypeInfoVTablePointer =
+          PointerAuthSchema(Key::ASDA, false, Discrimination::None);
+      Opts.CXXVTTVTablePointers =
+          PointerAuthSchema(Key::ASDA, false, Discrimination::None);
+      Opts.CXXVirtualFunctionPointers =
+          Opts.CXXVirtualVariadicFunctionPointers =
+              PointerAuthSchema(Key::ASIA, true, Discrimination::Decl);
     }
     return true;
   }
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index aec387153d87c..f96ddfcc8d834 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -42,6 +42,10 @@ typedef enum {
      The extra data is always 0. */
   ptrauth_key_function_pointer = ptrauth_key_process_independent_code,
 
+  /* The key used to sign C++ v-table pointers.
+     The extra data is always 0. */
+  ptrauth_key_cxx_vtable_pointer = ptrauth_key_process_independent_data,
+
   /* Other pointers signed under the ABI use private ABI rules. */
 
 } ptrauth_key;
@@ -213,6 +217,12 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 #define ptrauth_sign_generic_data(__value, __data)                             \
   __builtin_ptrauth_sign_generic_data(__value, __data)
 
+/* C++ vtable pointer signing class attribute */
+#define ptrauth_cxx_vtable_pointer(key, address_discrimination,                \
+                                   extra_discrimination...)                    \
+  [[clang::ptrauth_vtable_pointer(key, address_discrimination,                 \
+                                  extra_discrimination)]]
+
 #else
 
 #define ptrauth_strip(__value, __key)                                          \
@@ -279,6 +289,10 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
     ((ptrauth_generic_signature_t)0);                                          \
   })
 
+
+#define ptrauth_cxx_vtable_pointer(key, address_discrimination,                \
+                                   extra_discrimination...)
+
 #endif /* __has_feature(ptrauth_intrinsics) */
 
 #endif /* __PTRAUTH_H */
diff --git a/clang/lib/InstallAPI/Visitor.cpp b/clang/lib/InstallAPI/Visitor.cpp
index cf3aaa4c6ec93..2e3be225a6d7e 100644
--- a/clang/lib/InstallAPI/Visitor.cpp
+++ b/clang/lib/InstallAPI/Visitor.cpp
@@ -447,16 +447,16 @@ InstallAPIVisitor::getMangledCXXVTableName(const CXXRecordDecl *D) const {
   return getBackendMangledName(Name);
 }
 
-std::string
-InstallAPIVisitor::getMangledCXXThunk(const GlobalDecl &D,
-                                      const ThunkInfo &Thunk) const {
+std::string InstallAPIVisitor::getMangledCXXThunk(
+    const GlobalDecl &D, const ThunkInfo &Thunk, bool ElideOverrideInfo) const {
   SmallString<256> Name;
   raw_svector_ostream NameStream(Name);
   const auto *Method = cast<CXXMethodDecl>(D.getDecl());
   if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method))
-    MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk.This, NameStream);
+    MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk, ElideOverrideInfo,
+                           NameStream);
   else
-    MC->mangleThunk(Method, Thunk, NameStream);
+    MC->mangleThunk(Method, Thunk, ElideOverrideInfo, NameStream);
 
   return getBackendMangledName(Name);
 }
@@ -500,7 +500,8 @@ void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
             return;
 
           for (const auto &Thunk : *Thunks) {
-            const std::string Name = getMangledCXXThunk(GD, Thunk);
+            const std::string Name =
+                getMangledCXXThunk(GD, Thunk, /*ElideOverrideInfo=*/true);
             auto [GR, FA] = Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
                                                  GlobalRecord::Kind::Function,
                                                  Avail, GD.getDecl(), Access);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index c528917437332..a07f7ad2233fe 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -368,6 +368,27 @@ static bool attributeIsTypeArgAttr(const IdentifierInfo &II) {
 #undef CLANG_ATTR_TYPE_ARG_LIST
 }
 
+/// Determine whether the given attribute takes identifier arguments.
+static bool attributeHasStrictIdentifierArgs(const IdentifierInfo &II) {
+#define CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST
+  return (llvm::StringSwitch<uint64_t>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+              .Default(0)) != 0;
+#undef CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST
+}
+
+/// Determine whether the given attribute takes an identifier argument at a
+/// specific index
+static bool attributeHasStrictIdentifierArgAtIndex(const IdentifierInfo &II,
+                                                   size_t argIndex) {
+#define CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST
+  return (llvm::StringSwitch<uint64_t>(normalizeAttrName(II.getName()))
+#include "clang/Parse/AttrParserStringSwitches.inc"
+              .Default(0)) &
+         (1ull << argIndex);
+#undef CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST
+}
+
 /// Determine whether the given attribute requires parsing its arguments
 /// in an unevaluated context or not.
 static bool attributeParsedArgsUnevaluated(const IdentifierInfo &II) {
@@ -546,7 +567,8 @@ unsigned Parser::ParseAttributeArgsCommon(
       }
       if (T.isUsable())
         TheParsedType = T.get();
-    } else if (AttributeHasVariadicIdentifierArg) {
+    } else if (AttributeHasVariadicIdentifierArg ||
+               attributeHasStrictIdentifierArgs(*AttrName)) {
       // Parse variadic identifier arg. This can either consume identifiers or
       // expressions. Variadic identifier args do not support parameter packs
       // because those are typically used for attributes with enumeration
@@ -557,6 +579,12 @@ unsigned Parser::ParseAttributeArgsCommon(
         if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
           Tok.setKind(tok::identifier);
 
+        if (Tok.is(tok::identifier) && attributeHasStrictIdentifierArgAtIndex(
+                                           *AttrName, ArgExprs.size())) {
+          ArgExprs.push_back(ParseIdentifierLoc());
+          continue;
+        }
+
         ExprResult ArgExpr;
         if (Tok.is(tok::identifier)) {
           ArgExprs.push_back(ParseIdentifierLoc());
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 5041fd65286fa..82c4a3c866458 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -9149,6 +9149,118 @@ EnforceTCBLeafAttr *Sema::mergeEnforceTCBLeafAttr(
       *this, D, AL);
 }
 
+static void handleVTablePointerAuthentication(Sema &S, Decl *D,
+                                              const ParsedAttr &AL) {
+  CXXRecordDecl *decl = cast<CXXRecordDecl>(D);
+  const uint32_t numArgs = AL.getNumArgs();
+  if (numArgs > 4) {
+    S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 4;
+    AL.setInvalid();
+  }
+
+  if (numArgs == 0) {
+    S.Diag(AL.getLoc(), diag::err_attribute_too_few_arguments) << AL;
+    AL.setInvalid();
+    return;
+  }
+
+  if (D->getAttr<VTablePointerAuthenticationAttr>()) {
+    S.Diag(AL.getLoc(), diag::err_duplicated_vtable_pointer_auth) << decl;
+    AL.setInvalid();
+  }
+
+  auto keyType = VTablePointerAuthenticationAttr::VPtrAuthKeyType::DefaultKey;
+  if (AL.isArgIdent(0)) {
+    IdentifierLoc *IL = AL.getArgAsIdent(0);
+    if (!VTablePointerAuthenticationAttr::ConvertStrToVPtrAuthKeyType(
+            IL->Ident->getName(), keyType)) {
+      S.Diag(IL->Loc, diag::err_invalid_authentication_key) << IL->Ident;
+      AL.setInvalid();
+    }
+    if (keyType == VTablePointerAuthenticationAttr::DefaultKey &&
+        !S.getLangOpts().PointerAuthCalls) {
+      S.Diag(AL.getLoc(), diag::err_no_default_vtable_pointer_auth) << 0;
+      AL.setInvalid();
+    }
+  } else {
+    S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
+        << AL << AANT_ArgumentIdentifier;
+    return;
+  }
+
+  auto addressDiversityMode = VTablePointerAuthenticationAttr::
+      AddressDiscriminationMode::DefaultAddressDiscrimination;
+  if (AL.getNumArgs() > 1) {
+    if (AL.isArgIdent(1)) {
+      IdentifierLoc *IL = AL.getArgAsIdent(1);
+      if (!VTablePointerAuthenticationAttr::
+              ConvertStrToAddressDiscriminationMode(IL->Ident->getName(),
+                                                    addressDiversityMode)) {
+        S.Diag(IL->Loc, diag::err_invalid_address_discrimination) << IL->Ident;
+        AL.setInvalid();
+      }
+      if (addressDiversityMode ==
+              VTablePointerAuthenticationAttr::DefaultAddressDiscrimination &&
+          !S.getLangOpts().PointerAuthCalls) {
+        S.Diag(IL->Loc, diag::err_no_default_vtable_pointer_auth) << 1;
+        AL.setInvalid();
+      }
+    } else {
+      S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
+          << AL << AANT_ArgumentIdentifier;
+    }
+  }
+
+  auto extraDiscrimination = VTablePointerAuthenticationAttr::
+      ExtraDiscrimination::DefaultExtraDiscrimination;
+  if (AL.getNumArgs() > 2) {
+    if (AL.isArgIdent(2)) {
+      IdentifierLoc *IL = AL.getArgAsIdent(2);
+      if (!VTablePointerAuthenticationAttr::ConvertStrToExtraDiscrimination(
+              IL->Ident->getName(), extraDiscrimination)) {
+        S.Diag(IL->Loc, diag::err_invalid_extra_discrimination) << IL->Ident;
+        AL.setInvalid();
+      }
+      if (extraDiscrimination ==
+              VTablePointerAuthenticationAttr::DefaultExtraDiscrimination &&
+          !S.getLangOpts().PointerAuthCalls) {
+        S.Diag(AL.getLoc(), diag::err_no_default_vtable_pointer_auth) << 2;
+        AL.setInvalid();
+      }
+    } else {
+      S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
+          << AL << AANT_ArgumentIdentifier;
+    }
+  }
+
+  uint32_t customDiscriminationValue = 0;
+  if (extraDiscrimination ==
+      VTablePointerAuthenticationAttr::CustomDiscrimination) {
+    if (numArgs < 4) {
+      S.Diag(AL.getLoc(), diag::err_missing_custom_discrimination) << AL << 4;
+      AL.setInvalid();
+      return;
+    }
+    if (numArgs > 4) {
+      S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 4;
+      AL.setInvalid();
+    }
+
+    if (!AL.isArgExpr(3) || !S.checkUInt32Argument(AL, AL.getArgAsExpr(3),
+                                                   customDiscriminationValue)) {
+      S.Diag(AL.getLoc(), diag::err_invalid_custom_discrimination);
+      AL.setInvalid();
+    }
+  } else if (numArgs > 3) {
+    S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 3;
+    AL.setInvalid();
+  }
+
+  decl->addAttr(::new (S.Context) VTablePointerAuthenticationAttr(
+      S.Context, AL, keyType, addressDiversityMode, extraDiscrimination,
+      customDiscriminationValue));
+}
+
 //===----------------------------------------------------------------------===//
 // Top Level Sema Entry Points
 //===----------------------------------------------------------------------===//
@@ -10093,6 +10205,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
   case ParsedAttr::AT_TypeNullable:
     handleNullableTypeAttr(S, D, AL);
     break;
+
+  case ParsedAttr::AT_VTablePointerAuthentication:
+    handleVTablePointerAuthentication(S, D, AL);
+    break;
   }
 }
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 8ab429e2a136e..81a705dcb6f41 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7119,6 +7119,11 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
     return false;
   };
 
+  if (!Record->isInvalidDecl() &&
+      Record->hasAttr<VTablePointerAuthenticationAttr>()) {
+    checkIncorrectVTablePointerAuthenticationAttribute(*Record);
+  }
+
   auto CompleteMemberFunction = [&](CXXMethodDecl *M) {
     // Check whether the explicitly-defaulted members are valid.
     bool Incomplete = CheckForDefaultedFunction(M);
@@ -10503,6 +10508,47 @@ void Sema::checkIllFormedTrivialABIStruct(CXXRecordDecl &RD) {
   }
 }
 
+void Sema::checkIncorrectVTablePointerAuthenticationAttribute(
+    CXXRecordDecl &RD) {
+  if (RequireCompleteType(RD.getLocation(), Context.getRecordType(&RD),
+                          diag::err_incomplete_type_vtable_pointer_auth)) {
+    return;
+  }
+
+  const CXXRecordDecl *primaryBase = &RD;
+  if (primaryBase->hasAnyDependentBases()) {
+    return;
+  }
+
+  while (1) {
+    assert(primaryBase);
+    const CXXRecordDecl *base = nullptr;
+    for (auto basePtr : primaryBase->bases()) {
+      if (!basePtr.getType()->getAsCXXRecordDecl()->isDynamicClass())
+        continue;
+      base = basePtr.getType()->getAsCXXRecordDecl();
+      break;
+    }
+    if (!base || base == primaryBase || !base->isPolymorphic())
+      break;
+    if (base->isPolymorphic()) {
+      Diag(RD.getAttr<VTablePointerAuthenticationAttr>()->getLocation(),
+           diag::err_non_top_level_vtable_pointer_auth)
+          << &RD << base;
+    } else {
+      break;
+    }
+    primaryBase = base;
+  }
+
+  if (!RD.isPolymorphic()) {
+    Diag(RD.getAttr<VTablePointerAuthenticationAttr>()->getLocation(),
+         diag::err_non_polymorphic_vtable_pointer_auth)
+        << &RD;
+    return;
+  }
+}
+
 void Sema::ActOnFinishCXXMemberSpecification(
     Scope *S, SourceLocation RLoc, Decl *TagDecl, SourceLocation LBrac,
     SourceLocation RBrac, const ParsedAttributesView &AttrList) {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ded4f59833ac0..4b09b5b274f54 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14201,6 +14201,39 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
 
     QualType MPTy = Context.getMemberPointerType(
         op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr());
+
+    if (getLangOpts().PointerAuthCalls && MD->isVirtual() &&
+        !isUnevaluatedContext() && !MPTy->isDependentType()) {
+      // When pointer authentication is enabled, argument and return types of
+      // vitual member functions must be complete. This is because vitrual
+      // member function pointers are implemented using virtual dispatch
+      // thunks and the thunks cannot be emitted if the argument or return
+      // types are incomplete.
+      auto ReturnOrParamTypeIsIncomplete = [&](QualType T,
+                                               SourceLocation DeclRefLoc,
+                                               SourceLocation RetArgTypeLoc) {
+        if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) {
+          Diag(DeclRefLoc,
+               diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret);
+          Diag(RetArgTypeLoc,
+               diag::note_ptrauth_virtual_function_incomplete_arg_ret_type)
+              << T;
+          return true;
+        }
+        return false;
+      };
+      QualType RetTy = MD->getReturnType();
+      bool IsIncomplete =
+          !RetTy->isVoidType() &&
+          ReturnOrParamTypeIsIncomplete(
+              RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin());
+      for (auto *PVD : MD->parameters())
+        IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc,
+                                                      PVD->getBeginLoc());
+      if (IsIncomplete)
+        return QualType();
+    }
+
     // Under the MS ABI, lock down the inheritance model now.
     if (Context.getTargetInfo().getCXXABI().isMicrosoft())
       (void)isCompleteType(OpLoc, MPTy);
diff --git a/clang/test/CodeGen/ptrauth-ubsan-vptr.cpp b/clang/test/CodeGen/ptrauth-ubsan-vptr.cpp
new file mode 100644
index 0000000000000..6c36004641477
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-ubsan-vptr.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple arm64e-apple-ios15 -fsanitize=vptr -O0 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64e-apple-ios15 -fsanitize=vptr -O2 -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+struct S {
+  S() {}
+  ~S() {}
+  virtual int v() { return 0; }
+  int a;
+};
+
+struct T : public S {
+  virtual int v();
+};
+
+// CHECK-LABEL: foo1
+int foo1(void* Buffer) {
+  T *p = reinterpret_cast<T*>(Buffer);
+  return p->v();
+}
+// CHECK-NOT: call {{.*}} @llvm.ptrauth.auth{{.*}}
+// CHECK-NOT: call {{.*}} @llvm.ptrauth.strip{{.*}}
+
+// CHECK-LABEL: foo2
+int foo2(S* s) {
+  T *p = dynamic_cast<T*>(s);
+  return p->v();
+}
+
+// CHECK-NOT: call {{.*}} @llvm.ptrauth.auth{{.*}}
+// CHECK-NOT: call {{.*}} @llvm.ptrauth.strip{{.*}}
diff --git a/clang/test/CodeGenCXX/catch-undef-behavior.cpp b/clang/test/CodeGenCXX/catch-undef-behavior.cpp
index 6fd7d16f86369..a26069f8ea39b 100644
--- a/clang/test/CodeGenCXX/catch-undef-behavior.cpp
+++ b/clang/test/CodeGenCXX/catch-undef-behavior.cpp
@@ -56,8 +56,8 @@ void member_access(S *p) {
 
   // (1b) Check that 'p' actually points to an 'S'.
 
-  // CHECK: %[[VPTR:.*]] = load i64, ptr
-  //
+  // CHECK: %[[VTABLE:.*]] = load ptr, ptr %0
+  // CHECK: %[[VPTR:.*]] = ptrtoint ptr %[[VTABLE]] to i64
   // hash_16_bytes:
   //
   // If this number changes, it indicates that either the mangled name of ::S
@@ -115,7 +115,8 @@ void member_access(S *p) {
 
   // (3b) Check that 'p' actually points to an 'S'
 
-  // CHECK: load i64, ptr
+  // CHECK: [[VTABLE2:%.*]] = load ptr, ptr
+  // CHECK: ptrtoint ptr [[VTABLE2]] to i64
   // CHECK-NEXT: xor i64 {{-4030275160588942838|1107558922}},
   // [...]
   // CHECK: getelementptr inbounds [128 x i64], ptr @__ubsan_vptr_type_cache, i32 0, i64 %
diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp
new file mode 100644
index 0000000000000..c2f20d56b0a6b
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp
@@ -0,0 +1,105 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -fno-rtti -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @_ZTV1A = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZNK1A3abcEv, i32 0, i64 12401, ptr getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2)), ptr null] }, align 8
+// CHECK: @_ZTV4Base = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZNK4Base3abcEv, i32 0, i64 64320, ptr getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV4Base, i32 0, i32 0, i32 2)), ptr null] }, align 8
+// CHECK: @_ZTV8Derived2 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr ptrauth (ptr @_ZNK8Derived23efgEv, i32 0, i64 36603, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV8Derived2, i32 0, i32 0, i32 3)), ptr null] }, align 8
+// CHECK: @_ZTV2D2 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr ptrauth (ptr @_ZNK2D23abcEv, i32 0, i64 20222, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 3)), ptr null] }, align 8
+
+struct A {
+  virtual const char* abc(void) const;
+};
+
+const char* A::abc(void) const {return "A"; };
+
+struct B : virtual A {
+  virtual void VF();
+};
+
+void B::VF() {}
+
+void FUNC(B* p) {
+// CHECK: [[T1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV1A, i64 2)
+// CHECK-NEXT:  [[BT1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV1A, i64 2) to i64), i64 12401)
+// CHECK-NEXT:  [[T2:%.*]] = call noundef ptr [[T1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BT1]]) ]
+  const char* c = p->A::abc();
+}
+
+
+// Test2
+struct Base { virtual char* abc(void) const; };
+
+char* Base::abc() const { return 0; }
+
+struct Derived : public Base {
+};
+
+void FUNC1(Derived* p) {
+// CHECK: [[U1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2)
+// CHECK-NEXT: [[BU1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2) to i64), i64 64320)
+// CHECK-NEXT:  [[U2:%.*]] = call noundef ptr [[U1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BU1]]) ]
+  char* c = p->Base::abc();
+}
+
+
+// Test3
+struct Base2 { };
+
+struct Derived2 : virtual Base2 {
+  virtual char* efg(void) const;
+};
+
+char* Derived2::efg(void) const { return 0; }
+
+void FUNC2(Derived2* p) {
+// CHECK: [[V1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV8Derived2, i64 3)
+// CHECK-NEXT:  [[BV1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV8Derived2, i64 3) to i64), i64 36603)
+// CHECK-NEXT:  [[V2:%.*]] = call noundef ptr [[V1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BV1]]) ]
+  char* c = p->Derived2::efg();
+}
+
+// Test4
+struct Base3 { };
+
+struct D1 : virtual Base3 {
+};
+
+struct D2 : virtual Base3 {
+ virtual char *abc(void) const;
+};
+
+struct Sub : D1, D2 {
+};
+
+char* D2::abc(void) const { return 0; }
+
+void FUNC3(Sub* p) {
+// CHECK: [[W1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2D2, i64 3)
+// CHECK-NEXT:  [[BW1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2D2, i64 3) to i64), i64 20222)
+// CHECK-NEXT:  [[W2:%.*]] = call noundef ptr [[W1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BW1]]) ]
+  char* c = p->D2::abc();
+}
+
+
+// Test4
+struct Base4 { virtual void abc(); };
+
+void Base4::abc() {}
+
+struct Derived4 : public Base4 {
+  void abc() override;
+};
+
+void Derived4::abc() {}
+
+void FUNC4(Derived4* p) {
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 426)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+  p->abc();
+}
diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp
new file mode 100644
index 0000000000000..996829a14d7d1
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5TemplIiE, ptr ptrauth (ptr @_ZN5TemplIiE1fEv, i32 0, i64 22189, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 2)), ptr ptrauth (ptr @_ZN5TemplIiE1gEv, i32 0, i64 9912, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 3)), ptr null] }, align 8
+
+struct Base {
+  virtual void abc(void) const;
+};
+
+void Base::abc(void) const {}
+
+void FUNC(Base* p) {
+  p->Base::abc();
+}
+
+// CHECK: getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2)
+// CHECK-NOT: call void @_ZNK4Base3abcEv
+
+template<class T>
+struct Templ {
+  virtual void f() {}
+  virtual void g() {}
+};
+template<class T>
+struct SubTempl : public Templ<T> {
+  virtual void f() {} // override
+  virtual void g() {} // override
+};
+
+void f(SubTempl<int>* t) {
+  // Qualified calls go through the (qualified) vtable in apple-kext mode.
+  // Since t's this pointer points to SubTempl's vtable, the call needs
+  // to load Templ<int>'s vtable.  Hence, Templ<int>::g needs to be
+  // instantiated in this TU, for it's referenced by the vtable.
+  // (This happens only in apple-kext mode; elsewhere virtual calls can always
+  // use the vtable pointer off this instead of having to load the vtable
+  // symbol.)
+  t->Templ::f();
+}
+
+// CHECK: getelementptr inbounds (ptr, ptr @_ZTV5TemplIiE, i64 2)
+// CHECK: define internal void @_ZN5TemplIiE1fEv(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %this)
+// CHECK: define internal void @_ZN5TemplIiE1gEv(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %this)
diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp
new file mode 100644
index 0000000000000..7bcf1fbfdb9de
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++98 -fptrauth-calls -fapple-kext -fno-rtti -disable-O0-optnone -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZN5TemplIiED1Ev, i32 0, i64 57986, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 2)), ptr ptrauth (ptr @_ZN5TemplIiED0Ev, i32 0, i64 22856, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 3)), ptr ptrauth (ptr @_ZN5TemplIiE1fEv, i32 0, i64 22189, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 4)), ptr ptrauth (ptr @_ZN5TemplIiE1gEv, i32 0, i64 9912, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 5)), ptr null] }, align 8
+
+struct B1 {
+  virtual ~B1();
+};
+
+B1::~B1() {}
+
+void DELETE(B1 *pb1) {
+  pb1->B1::~B1();
+}
+// CHECK-LABEL: define void @_ZN2B1D0Ev
+// CHECK: [[T1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2)
+// CHECK-NEXT: [[B1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) to i64), i64 14635)
+// CHECK-NEXT: call noundef ptr [[T1]](ptr noundef nonnull align 8 dereferenceable(8) [[T2:%.*]]) [ "ptrauth"(i32 0, i64 [[B1]]) ]
+// CHECK-LABEL: define void @_Z6DELETEP2B1
+// CHECK: [[T3:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2)
+// CHECK-NEXT: [[B3:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) to i64), i64 14635)
+// CHECK-NEXT:  call noundef ptr [[T3]](ptr noundef nonnull align 8 dereferenceable(8) [[T4:%.*]]) [ "ptrauth"(i32 0, i64 [[B3]])
+
+template<class T>
+struct Templ {
+  virtual ~Templ(); // Out-of-line so that the destructor doesn't cause a vtable
+  virtual void f() {}
+  virtual void g() {}
+};
+template<class T>
+struct SubTempl : public Templ<T> {
+  virtual ~SubTempl() {} // override
+  virtual void f() {} // override
+  virtual void g() {} // override
+};
+
+void f(SubTempl<int>* t) {
+  // Qualified calls go through the (qualified) vtable in apple-kext mode.
+  // Since t's this pointer points to SubTempl's vtable, the call needs
+  // to load Templ<int>'s vtable.  Hence, Templ<int>::g needs to be
+  // instantiated in this TU, for it's referenced by the vtable.
+  // (This happens only in apple-kext mode; elsewhere virtual calls can always
+  // use the vtable pointer off this instead of having to load the vtable
+  // symbol.)
+  t->Templ::~Templ();
+}
+
+// CHECK: getelementptr inbounds (ptr, ptr @_ZTV5TemplIiE, i64 2)
+// CHECK: declare void @_ZN5TemplIiED0Ev(ptr noundef nonnull align 8 dereferenceable(8))
+// CHECK: define internal void @_ZN5TemplIiE1fEv(ptr noundef nonnull align 8 dereferenceable(8) %this)
+// CHECK: define internal void @_ZN5TemplIiE1gEv(ptr noundef nonnull align 8 dereferenceable(8) %this)
diff --git a/clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp b/clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp
new file mode 100644
index 0000000000000..16d0ade0e8e74
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp
@@ -0,0 +1,652 @@
+// RUN: %clang_cc1 %s -x c++ -std=c++11  -triple arm64-apple-ios -fptrauth-intrinsics -fptrauth-calls -emit-llvm -O1 -disable-llvm-passes  -o - | FileCheck  --check-prefix=CHECK-DEFAULT-NONE %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11  -triple arm64-apple-ios -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck --check-prefix=CHECK-DEFAULT-TYPE %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11  -triple arm64-apple-ios -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck --check-prefix=CHECK-DEFAULT-ADDRESS %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11  -triple arm64-apple-ios -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck --check-prefix=CHECK-DEFAULT-BOTH %s
+#include <ptrauth.h>
+namespace test1 {
+
+#define authenticated(a...) ptrauth_cxx_vtable_pointer(a)
+
+struct NoExplicitAuth {
+  virtual ~NoExplicitAuth();
+  virtual void f();
+  virtual void g();
+};
+
+struct authenticated(no_authentication, no_address_discrimination, no_extra_discrimination) ExplicitlyDisableAuth {
+  virtual ~ExplicitlyDisableAuth();
+  virtual void f();
+  virtual void g();
+};
+
+struct authenticated(default_key, address_discrimination, default_extra_discrimination) ExplicitAddressDiscrimination {
+  virtual ~ExplicitAddressDiscrimination();
+  virtual void f();
+  virtual void g();
+};
+
+struct authenticated(default_key, no_address_discrimination, default_extra_discrimination) ExplicitNoAddressDiscrimination {
+  virtual ~ExplicitNoAddressDiscrimination();
+  virtual void f();
+  virtual void g();
+};
+
+struct authenticated(default_key, default_address_discrimination, no_extra_discrimination) ExplicitNoExtraDiscrimination {
+  virtual ~ExplicitNoExtraDiscrimination();
+  virtual void f();
+  virtual void g();
+};
+
+struct authenticated(default_key, default_address_discrimination, type_discrimination) ExplicitTypeDiscrimination {
+  virtual ~ExplicitTypeDiscrimination();
+  virtual void f();
+  virtual void g();
+};
+
+struct authenticated(default_key, default_address_discrimination, custom_discrimination, 0xf00d) ExplicitCustomDiscrimination {
+  virtual ~ExplicitCustomDiscrimination();
+  virtual void f();
+  virtual void g();
+};
+
+template <typename T>
+struct SubClass : T {
+  virtual void g();
+  virtual T *h();
+};
+
+template <typename T>
+SubClass<T> *make_subclass(T *);
+
+struct authenticated(default_key, address_discrimination, type_discrimination) BasicStruct {
+  virtual ~BasicStruct();
+};
+
+template <typename T>
+struct PrimaryBasicStruct : BasicStruct, T {};
+template <typename T>
+struct PrimaryBasicStruct<T> *make_primary_basic(T *);
+template <typename T>
+struct SecondaryBasicStruct : T, BasicStruct {};
+template <typename T>
+struct SecondaryBasicStruct<T> *make_secondary_basic(T *);
+template <typename T>
+struct VirtualSubClass : virtual T {
+  virtual void g();
+  virtual T *h();
+};
+template <typename T>
+struct VirtualPrimaryStruct : virtual T, VirtualSubClass<T> {};
+template <typename T>
+struct VirtualPrimaryStruct<T> *make_virtual_primary(T *);
+template <typename T>
+struct VirtualSecondaryStruct : VirtualSubClass<T>, virtual T {};
+template <typename T>
+struct VirtualSecondaryStruct<T> *make_virtual_secondary(T *);
+
+// CHECK-DEFAULT-NONE: _ZN5test14testEPNS_14NoExplicitAuthEPNS_21ExplicitlyDisableAuthEPNS_29ExplicitAddressDiscriminationEPNS_31ExplicitNoAddressDiscriminationEPNS_29ExplicitNoExtraDiscriminationEPNS_26ExplicitTypeDiscriminationEPNS_28ExplicitCustomDiscriminationE
+void test(NoExplicitAuth *a, ExplicitlyDisableAuth *b, ExplicitAddressDiscrimination *c,
+          ExplicitNoAddressDiscrimination *d, ExplicitNoExtraDiscrimination *e,
+          ExplicitTypeDiscrimination *f, ExplicitCustomDiscrimination *g) {
+  a->f();
+  // CHECK-DEFAULT-NONE: %0 = load ptr, ptr %a.addr, align 8, !tbaa !2
+  // CHECK-DEFAULT-NONE: %vtable = load ptr, ptr %0, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %1 = ptrtoint ptr %vtable to i64
+  // CHECK-DEFAULT-NONE: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 0)
+  // CHECK-DEFAULT-NONE: %3 = inttoptr i64 %2 to ptr
+  // CHECK-DEFAULT-NONE: %vfn = getelementptr inbounds ptr, ptr %3, i64 2
+
+  b->f();
+  // CHECK-DEFAULT-NONE: %7 = load ptr, ptr %b.addr, align 8, !tbaa !2
+  // CHECK-DEFAULT-NONE: %vtable1 = load ptr, ptr %7, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %vfn2 = getelementptr inbounds ptr, ptr %vtable1, i64 2
+
+  c->f();
+  // CHECK-DEFAULT-NONE: %11 = load ptr, ptr %c.addr, align 8, !tbaa !2
+  // CHECK-DEFAULT-NONE: %vtable3 = load ptr, ptr %11, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %12 = ptrtoint ptr %11 to i64
+  // CHECK-DEFAULT-NONE: %13 = ptrtoint ptr %vtable3 to i64
+  // CHECK-DEFAULT-NONE: %14 = call i64 @llvm.ptrauth.auth(i64 %13, i32 2, i64 %12)
+  // CHECK-DEFAULT-NONE: %15 = inttoptr i64 %14 to ptr
+  // CHECK-DEFAULT-NONE: %vfn4 = getelementptr inbounds ptr, ptr %15, i64 2
+
+  d->f();
+  // CHECK-DEFAULT-NONE: %19 = load ptr, ptr %d.addr, align 8, !tbaa !2
+  // CHECK-DEFAULT-NONE: %vtable5 = load ptr, ptr %19, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %20 = ptrtoint ptr %vtable5 to i64
+  // CHECK-DEFAULT-NONE: %21 = call i64 @llvm.ptrauth.auth(i64 %20, i32 2, i64 0)
+  // CHECK-DEFAULT-NONE: %22 = inttoptr i64 %21 to ptr
+  // CHECK-DEFAULT-NONE: %vfn6 = getelementptr inbounds ptr, ptr %22, i64 2
+
+  e->f();
+  // CHECK-DEFAULT-NONE: %26 = load ptr, ptr %e.addr, align 8, !tbaa !2
+  // CHECK-DEFAULT-NONE: %vtable7 = load ptr, ptr %26, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %27 = ptrtoint ptr %vtable7 to i64
+  // CHECK-DEFAULT-NONE: %28 = call i64 @llvm.ptrauth.auth(i64 %27, i32 2, i64 0)
+  // CHECK-DEFAULT-NONE: %29 = inttoptr i64 %28 to ptr
+  // CHECK-DEFAULT-NONE: %vfn8 = getelementptr inbounds ptr, ptr %29, i64 2
+
+  f->f();
+  // CHECK-DEFAULT-NONE: %33 = load ptr, ptr %f.addr, align 8, !tbaa !2
+  // CHECK-DEFAULT-NONE: %vtable9 = load ptr, ptr %33, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %34 = ptrtoint ptr %vtable9 to i64
+  // CHECK-DEFAULT-NONE: %35 = call i64 @llvm.ptrauth.auth(i64 %34, i32 2, i64 6177)
+  // CHECK-DEFAULT-NONE: %36 = inttoptr i64 %35 to ptr
+  // CHECK-DEFAULT-NONE: %vfn10 = getelementptr inbounds ptr, ptr %36, i64 2
+ 
+
+  g->f();
+  // CHECK-DEFAULT-NONE: %40 = load ptr, ptr %g.addr, align 8, !tbaa !2
+  // CHECK-DEFAULT-NONE: %vtable11 = load ptr, ptr %40, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %41 = ptrtoint ptr %vtable11 to i64
+  // CHECK-DEFAULT-NONE: %42 = call i64 @llvm.ptrauth.auth(i64 %41, i32 2, i64 61453)
+  // CHECK-DEFAULT-NONE: %43 = inttoptr i64 %42 to ptr
+  // CHECK-DEFAULT-NONE: %vfn12 = getelementptr inbounds ptr, ptr %43, i64 2
+
+  // basic subclass
+  make_subclass(a)->f();
+  // CHECK-DEFAULT-NONE: %vtable13 = load ptr, ptr %call, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %48 = ptrtoint ptr %vtable13 to i64
+  // CHECK-DEFAULT-NONE: %49 = call i64 @llvm.ptrauth.auth(i64 %48, i32 2, i64 0)
+  // CHECK-DEFAULT-NONE: %50 = inttoptr i64 %49 to ptr
+  // CHECK-DEFAULT-NONE: %vfn14 = getelementptr inbounds ptr, ptr %50, i64 2
+ 
+
+  make_subclass(a)->g();
+  // CHECK-DEFAULT-NONE: %vtable16 = load ptr, ptr %call15, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %55 = ptrtoint ptr %vtable16 to i64
+  // CHECK-DEFAULT-NONE: %56 = call i64 @llvm.ptrauth.auth(i64 %55, i32 2, i64 0)
+  // CHECK-DEFAULT-NONE: %57 = inttoptr i64 %56 to ptr
+  // CHECK-DEFAULT-NONE: %vfn17 = getelementptr inbounds ptr, ptr %57, i64 3
+
+  make_subclass(a)->h();
+  // CHECK-DEFAULT-NONE: %vtable19 = load ptr, ptr %call18, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %62 = ptrtoint ptr %vtable19 to i64
+  // CHECK-DEFAULT-NONE: %63 = call i64 @llvm.ptrauth.auth(i64 %62, i32 2, i64 0)
+  // CHECK-DEFAULT-NONE: %64 = inttoptr i64 %63 to ptr
+  // CHECK-DEFAULT-NONE: %vfn20 = getelementptr inbounds ptr, ptr %64, i64 4
+
+  make_subclass(b)->f();
+  // CHECK-DEFAULT-NONE: %vtable23 = load ptr, ptr %call22, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %vfn24 = getelementptr inbounds ptr, ptr %vtable23, i64 2
+
+  make_subclass(b)->g();
+  // CHECK-DEFAULT-NONE: %vtable26 = load ptr, ptr %call25, align 8, !tbaa !6
+  // CHECK-DEFAULT-NONE: %vfn27 = getelementptr inbounds ptr, ptr %vtable26, i64 3
+
+  make_subclass(b)->h();
+
+  make_subclass(c)->f();
+  make_subclass(c)->g();
+  make_subclass(c)->h();
+
+  make_subclass(d)->f();
+  make_subclass(d)->g();
+  make_subclass(d)->h();
+
+  make_subclass(e)->f();
+  make_subclass(e)->g();
+  make_subclass(e)->h();
+
+  make_subclass(f)->f();
+  make_subclass(f)->g();
+  make_subclass(f)->h();
+
+  make_subclass(g)->f();
+  make_subclass(g)->g();
+  make_subclass(g)->h();
+
+  // Basic multiple inheritance
+  make_primary_basic(a)->f();
+  make_primary_basic(b)->f();
+  make_primary_basic(c)->f();
+  make_primary_basic(d)->f();
+  make_primary_basic(e)->f();
+  make_primary_basic(f)->f();
+  make_primary_basic(g)->f();
+  make_secondary_basic(a)->f();
+  make_secondary_basic(b)->f();
+  make_secondary_basic(c)->f();
+  make_secondary_basic(d)->f();
+  make_secondary_basic(e)->f();
+  make_secondary_basic(f)->f();
+  make_secondary_basic(g)->f();
+
+  // virtual inheritance
+  make_virtual_primary(a)->f();
+  make_virtual_primary(b)->f();
+  make_virtual_primary(c)->f();
+  make_virtual_primary(d)->f();
+  make_virtual_primary(e)->f();
+  make_virtual_primary(f)->f();
+  make_virtual_primary(g)->f();
+  make_virtual_secondary(a)->f();
+  make_virtual_secondary(b)->f();
+  make_virtual_secondary(c)->f();
+  make_virtual_secondary(d)->f();
+  make_virtual_secondary(e)->f();
+  make_virtual_secondary(f)->f();
+  make_virtual_secondary(g)->f();
+}
+} // namespace test1
+
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 37831)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 2191)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 44989)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 63209)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43275)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 19073)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 25182)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 23051)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 3267)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 57764)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 8498)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61320)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 7682)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 53776)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 49565)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6177)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 61453)
+// CHECK-DEFAULT-TYPE:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 37831)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 2191)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 44989)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 63209)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43275)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 19073)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 25182)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 23051)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 3267)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 57764)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 8498)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61320)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 7682)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 53776)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 0)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-ADDRESS:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 37831)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 2191)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 44989)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 63209)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43275)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 19073)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 25182)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 23051)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 3267)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 57764)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 8498)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61320)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 7682)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 53776)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 49565)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 27707)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 31119)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 56943)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 5268)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 6022)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 34147)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 39413)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 6177)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 29468)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 61453)
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T:%.*]], i32 2, i64 [[T:%.*]])
+// CHECK-DEFAULT-BOTH:   [[T:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T:%.*]], i64 43175)
diff --git a/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp
new file mode 100644
index 0000000000000..b4a8784a33d8c
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 %s -I%S -triple=arm64-apple-ios -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck %s
+#include <typeinfo>
+
+struct A { int a; };
+
+// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr]
+// CHECK: @_ZTS1A = linkonce_odr hidden constant [3 x i8] c"1A\00"
+// CHECK: @_ZTI1A = linkonce_odr hidden constant { ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2), ptr inttoptr (i64 add (i64 ptrtoint (ptr @_ZTS1A to i64), i64 -9223372036854775808) to ptr) }
+
+auto ATI = typeid(A);
diff --git a/clang/test/CodeGenCXX/ptrauth-thunks.cpp b/clang/test/CodeGenCXX/ptrauth-thunks.cpp
new file mode 100644
index 0000000000000..a85c8c4c065c4
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-thunks.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck %s
+
+namespace Test1 {
+  struct B1 {
+    virtual void* foo1() {
+      return 0;
+    }
+  };
+  struct Pad1 {
+    virtual ~Pad1() {}
+  };
+  struct Proxy1 : Pad1, B1 {
+    virtual ~Proxy1() {}
+  };
+  struct D : virtual Proxy1 {
+    virtual ~D() {}
+    virtual void* foo1();
+  };
+  void* D::foo1() {
+    return (void*)this;
+  }
+}
+
+// CHECK-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test11DD0Ev(ptr noundef %this)
+// CHECK: %[[This:.*]] = load ptr
+// CHECK: %[[SignedVTable:.*]] = load ptr, ptr %[[This]], align 8
+// CHECK: %[[SignedVTableAsInt:.*]] = ptrtoint ptr %[[SignedVTable]] to i64
+// CHECK: %[[VTable:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[SignedVTableAsInt]], i32 2, i64 0)
diff --git a/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp
new file mode 100644
index 0000000000000..563d60780769b
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp
@@ -0,0 +1,581 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck %s
+
+// Check virtual function pointers in vtables are signed.
+
+// CHECK: %[[CLASS_B1:.*]] = type { ptr }
+
+// CHECK: @_ZTV2B1 = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI2B1, ptr ptrauth (ptr @_ZN2B12m0Ev, i32 0, i64 58196, ptr getelementptr inbounds ({ [3 x ptr] }, ptr @_ZTV2B1, i32 0, i32 0, i32 2))] }, align 8
+
+// CHECK: @g_B1 = global %class.B1 { ptr ptrauth (ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV2B1, i32 0, i32 0, i32 2), i32 2) }, align 8
+
+// CHECK: @_ZTV2B0 = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI2B0,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B0D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B0D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 6))] }, align 8
+
+// CHECK: @_ZTV2D0 = unnamed_addr constant { [9 x ptr] } { [9 x ptr] [ptr null, ptr @_ZTI2D0,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D02m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTch0_h4_N2D02m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D0D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D0D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D02m1Ev, i32 0, i64 35045, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D02m3Ev, i32 0, i64 10565, ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 8))] }, align 8
+
+// CHECK: @_ZTV2D1 = unnamed_addr constant { [8 x ptr] } { [8 x ptr] [ptr null, ptr @_ZTI2D1,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D12m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTch0_h4_N2D12m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D1D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D1D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D12m1Ev, i32 0, i64 52864, ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 7))] }, align 8
+
+// CHECK: @_ZTV2D2 = unnamed_addr constant { [9 x ptr], [8 x ptr] } { [9 x ptr] [ptr null, ptr @_ZTI2D2,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D22m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTch0_h4_N2D22m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D2D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D2D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D22m1Ev, i32 0, i64 35045, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D22m3Ev, i32 0, i64 10565, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 8))],
+// CHECK-SAME: [8 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2D2,
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D22m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTchn16_h4_N2D22m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D2D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D2D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D22m1Ev, i32 0, i64 52864, ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 7))] }, align 8
+
+// CHECK: @_ZTV2D3 = unnamed_addr constant { [7 x ptr], [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 32 to ptr), ptr null, ptr @_ZTI2D3,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D32m0Ev, i32 0, i64 44578, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D32m1Ev, i32 0, i64 30766, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D3D1Ev, i32 0, i64 57279, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2D3D0Ev, i32 0, i64 62452, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 6))],
+// CHECK-SAME: [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2D3,
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D32m0Ev, i32 0, i64 49430, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D32m1Ev, i32 0, i64 57119, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D3D1Ev, i32 0, i64 60799, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn16_N2D3D0Ev, i32 0, i64 52565, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 6))],
+// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr @_ZTI2D3,
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n24_N2D32m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTcv0_n32_h4_N2D32m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2D3D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2D3D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 10))] }, align 8
+
+// CHECK: @_ZTC2D30_2V0 = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 32 to ptr), ptr null, ptr @_ZTI2V0,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V02m0Ev, i32 0, i64 44578, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V02m1Ev, i32 0, i64 30766, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V0D1Ev, i32 0, i64 57279, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V0D0Ev, i32 0, i64 62452, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 6))],
+// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr @_ZTI2V0,
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n24_N2V02m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTcv0_n32_h4_N2V02m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V0D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V0D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 10))] }, align 8
+
+// CHECK: @_ZTC2D316_2V1 = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI2V1,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V12m0Ev, i32 0, i64 49430, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V12m1Ev, i32 0, i64 57119, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V1D1Ev, i32 0, i64 60799, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2V1D0Ev, i32 0, i64 52565, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 6))],
+// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2V1,
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n24_N2V12m0Ev, i32 0, i64 53119, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTcv0_n32_h4_N2V12m1Ev, i32 0, i64 15165, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN2B02m2Ev, i32 0, i64 43073, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V1D1Ev, i32 0, i64 25525, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N2V1D0Ev, i32 0, i64 21295, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 10))] }, align 8
+
+
+struct S0 {
+  int f;
+};
+
+struct S1 {
+  int f;
+};
+
+struct S2 : S0, S1 {
+  int f;
+};
+
+class B0 {
+public:
+  virtual void m0();
+  virtual S1 *m1();
+  virtual void m2();
+  virtual ~B0();
+  int f;
+};
+
+class B1 {
+public:
+  virtual void m0();
+};
+
+class D0 : public B0 {
+public:
+  void m0() override;
+  S2 *m1() override;
+  virtual void m3();
+  int f;
+};
+
+class D1 : public B0 {
+public:
+  void m0() override;
+  S2 *m1() override;
+  int f;
+};
+
+class D2 : public D0, public D1 {
+public:
+  void m0() override;
+  S2 *m1() override;
+  void m3() override;
+  int f;
+};
+
+class V0 : public virtual B0 {
+public:
+  void m0() override;
+  S2 *m1() override;
+  int f;
+};
+
+class V1 : public virtual B0 {
+public:
+  void m0() override;
+  S2 *m1() override;
+  ~V1();
+  int f;
+};
+
+class D3 : public V0, public V1 {
+public:
+  void m0() override;
+  S2 *m1() override;
+  int f;
+};
+
+B1 g_B1;
+
+void B0::m0() {}
+
+void B1::m0() {}
+
+void D0::m0() {}
+
+void D1::m0() {}
+
+void D2::m0() {}
+
+void D3::m0() {}
+
+V1::~V1() {
+  m1();
+}
+
+// Check sign/authentication of vtable pointers and authentication of virtual
+// functions.
+
+// CHECK-LABEL: define noundef ptr @_ZN2V1D2Ev(
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T1:[0-9]+]] = ptrtoint ptr %[[T0]] to i64
+// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T1]], i32 2, i64 0)
+// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to ptr
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[T3]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[T6]], i32 2, i64 0)
+// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T7]] to ptr
+// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS1]]
+
+// CHECK-LABEL: define void @_Z8testB0m0P2B0(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testB0m0(B0 *a) {
+  a->m0();
+}
+
+// CHECK-LABEL: define void @_Z8testB0m1P2B0(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 1
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 15165)
+// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testB0m1(B0 *a) {
+  a->m1();
+}
+
+// CHECK-LABEL: define void @_Z8testB0m2P2B0(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testB0m2(B0 *a) {
+  a->m2();
+}
+
+// CHECK-LABEL: define void @_Z8testD0m0P2D0(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD0m0(D0 *a) {
+  a->m0();
+}
+
+// CHECK-LABEL: define void @_Z8testD0m1P2D0(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 35045)
+// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD0m1(D0 *a) {
+  a->m1();
+}
+
+// CHECK-LABEL: define void @_Z8testD0m2P2D0(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD0m2(D0 *a) {
+  a->m2();
+}
+
+// CHECK-LABEL: define void @_Z8testD0m3P2D0(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 6
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 10565)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD0m3(D0 *a) {
+  a->m3();
+}
+
+
+// CHECK-LABEL: define void @_Z8testD1m0P2D1(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD1m0(D1 *a) {
+  a->m0();
+}
+
+// CHECK-LABEL: define void @_Z8testD1m1P2D1(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 52864)
+// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD1m1(D1 *a) {
+  a->m1();
+}
+
+// CHECK-LABEL: define void @_Z8testD1m2P2D1(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD1m2(D1 *a) {
+  a->m2();
+}
+
+
+// CHECK-LABEL: define void @_Z8testD2m0P2D2(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD2m0(D2 *a) {
+  a->m0();
+}
+
+// CHECK-LABEL: define void @_Z8testD2m1P2D2(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 35045)
+// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD2m1(D2 *a) {
+  a->m1();
+}
+
+// CHECK-LABEL: define void @_Z10testD2m2D0P2D2(
+// CHECK: call void @_ZN2B02m2Ev(ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}){{$}}
+
+void testD2m2D0(D2 *a) {
+  a->D0::m2();
+}
+
+// CHECK-LABEL: define void @_Z10testD2m2D1P2D2(
+// CHECK: call void @_ZN2B02m2Ev(ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}){{$}}
+
+void testD2m2D1(D2 *a) {
+  a->D1::m2();
+}
+
+// CHECK-LABEL: define void @_Z8testD2m3P2D2(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 6
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 10565)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD2m3(D2 *a) {
+  a->m3();
+}
+
+// CHECK-LABEL: define void @_Z8testD3m0P2D3(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 44578)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD3m0(D3 *a) {
+  a->m0();
+}
+
+// CHECK-LABEL: define void @_Z8testD3m1P2D3(
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 1
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 30766)
+// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD3m1(D3 *a) {
+  a->m1();
+}
+
+// CHECK: define void @_Z8testD3m2P2D3(ptr noundef %[[A:.*]])
+// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8
+// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[V0]], align 8
+// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0)
+// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr
+// CHECK: %[[VBASE_OFFSET_PTR:.*]] = getelementptr i8, ptr %[[V3]], i64 -24
+// CHECK: %[[VBASE_OFFSET:.*]] = load i64, ptr %[[VBASE_OFFSET_PTR]], align 8
+// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[VBASE_OFFSET]]
+// CHECK: %[[VTABLE1:.*]] = load ptr, ptr %[[ADD_PTR]], align 8
+// CHECK: %[[V4:.*]] = ptrtoint ptr %[[VTABLE1]] to i64
+// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 2, i64 0)
+// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr
+// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V6]], i64 2
+// CHECK: %[[V7:.*]] = load ptr, ptr %[[VFN]], align 8
+// CHECK: %[[V8:.*]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V8]], i64 43073)
+// CHECK: call void %[[V7]](ptr noundef nonnull align 8 dereferenceable(12) %[[ADD_PTR]]) [ "ptrauth"(i32 0, i64 %[[V9]]) ]
+
+void testD3m2(D3 *a) {
+  a->m2();
+}
+
+// CHECK-LABEL: define void @_Z17testD3Destructor0P2D3(
+// CHECK: load ptr, ptr
+// CHECK: %[[VTABLE:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T2:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 3
+// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 62452)
+// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD3Destructor0(D3 *a) {
+  delete a;
+}
+
+// CHECK-LABEL: define void @_Z17testD3Destructor1P2D3(
+// CHECK: %[[T6:.*]] = load ptr, ptr %
+// CHECK: %[[VTABLE0:[a-z0-9]+]] = load ptr, ptr %
+// CHECK: %[[T2:[0-9]+]] = ptrtoint ptr %[[VTABLE0]] to i64
+// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0)
+// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[COMPLETE_OFFSET_PTR:.*]] = getelementptr inbounds i64, ptr %[[T4]], i64 -2
+// CHECK: %[[T5:[0-9]+]] = load i64, ptr %[[COMPLETE_OFFSET_PTR]]
+// CHECK: %[[T7:[0-9]+]] = getelementptr inbounds i8, ptr %[[T6]], i64 %[[T5]]
+// CHECK: %[[VTABLE1:[a-z0-9]+]] = load ptr, ptr %[[T6]]
+// CHECK: %[[T9:[0-9]+]] = ptrtoint ptr %[[VTABLE1]] to i64
+// CHECK: %[[T10:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T9]], i32 2, i64 0)
+// CHECK: %[[T11:[0-9]+]] = inttoptr i64 %[[T10]] to ptr
+// CHECK: %[[VFN:[a-z0-9]+]] = getelementptr inbounds ptr, ptr %[[T11]], i64 2
+// CHECK: %[[T12:[0-9]+]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T13:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T14:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T13]], i64 57279)
+// CHECK: %call = call noundef ptr %[[T12]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T14]]) ]
+// CHECK: call void @_ZdlPv(ptr noundef %[[T7]])
+
+void testD3Destructor1(D3 *a) {
+  ::delete a;
+}
+
+// CHECK-LABEL: define void @_Z17testD3Destructor2P2D3(
+// CHECK: load ptr, ptr
+// CHECK: %[[VTABLE:.*]] = load ptr, ptr %
+// CHECK: %[[T2:.*]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0)
+// CHECK: %[[T4:.*]] = inttoptr i64 %[[T3]] to ptr
+// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2
+// CHECK: %[[T5:.*]] = load ptr, ptr %[[VFN]]
+// CHECK: %[[T6:.*]] = ptrtoint ptr %[[VFN]] to i64
+// CHECK: %[[T7:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 57279)
+// CHECK: %call = call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ]
+
+void testD3Destructor2(D3 *a) {
+  a->~D3();
+}
+
+void materializeConstructors() {
+  B0 B0;
+  B1 B1;
+  D0 D0;
+  D1 D1;
+  D2 D2;
+  D3 D3;
+  V0 V0;
+  V1 V1;
+}
+
+// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2B0C2Ev(
+// CHECK: %[[THIS:.*]] = load ptr, ptr %
+// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 40) ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 2) to i64), i32 2, i64 0)
+// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr
+// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]]
+
+// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D0C2Ev(
+// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 56) ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 2) to i64), i32 2, i64 0)
+// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr
+// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]]
+
+// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D1C2Ev(
+// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 48) ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 2) to i64), i32 2, i64 0)
+// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr
+// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]]
+
+// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D2C2Ev(
+// CHECK: %[[SLOT0:.*]] = load ptr, ptr
+// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 56) ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 2) to i64), i32 2, i64 0)
+// CHECK: %[[T1:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR0]] to ptr
+// CHECK: store ptr %[[T1]], ptr %[[SLOT0]]
+// CHECK: %[[T3:[a-z0-9.]+]] = getelementptr inbounds i8, ptr %[[SLOT0]], i64 16
+// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 48) ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 2) to i64), i32 2, i64 0)
+// CHECK: %[[T5:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR1]] to ptr
+// CHECK: store ptr %[[T5]], ptr %[[T3]]
+
+// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2V0C2Ev(
+// CHECK: %[[THIS1]] = load ptr, ptr %
+// CHECK: %[[VTT:[a-z0-9]+]] = load ptr, ptr %{{.*}}
+// CHECK: %[[T0:[0-9]+]] = load ptr, ptr %[[VTT]]
+// CHECK: %[[T1:[0-9]+]] = ptrtoint ptr %[[T0]] to i64
+// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T1]], i32 2, i64 0)
+// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to ptr
+// CHECK: %[[VTADDR0:[0-9]+]] = ptrtoint ptr %[[T3]] to i64
+// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[VTADDR0]], i32 2, i64 0)
+// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = inttoptr i64 %[[T7]] to ptr
+// CHECK: store ptr %[[SIGN_VTADDR0]], ptr %[[SLOT0]]
+// CHECK: %[[T9:[0-9]+]] = getelementptr inbounds ptr, ptr %[[VTT]], i64 1
+// CHECK: %[[T10:[0-9]+]] = load ptr, ptr %[[T9]]
+// CHECK: %[[T11:[0-9]+]] = ptrtoint ptr %[[T10]] to i64
+// CHECK: %[[T12:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T11]], i32 2, i64 0)
+// CHECK: %[[T13:[0-9]+]] = inttoptr i64 %[[T12]] to ptr
+// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %[[THIS1]]
+// CHECK: %[[T15:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64
+// CHECK: %[[T16:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T15]], i32 2, i64 0)
+// CHECK: %[[T17:[0-9]+]] = inttoptr i64 %[[T16]] to ptr
+// CHECK: %[[VBASE_OFFSET_PTR:[a-z.]+]] = getelementptr i8, ptr %[[T17]], i64 -24
+// CHECK: %[[VBASE_OFFSET:[a-z.]+]] = load i64, ptr %[[VBASE_OFFSET_PTR]]
+// CHECK: %[[T20:[a-z.]+]] = getelementptr inbounds i8, ptr %[[THIS1]], i64 %[[VBASE_OFFSET]]
+// CHECK: %[[VTADDR1:[0-9]+]] = ptrtoint ptr %[[T13]] to i64
+// CHECK: %[[T23:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[VTADDR1]], i32 2, i64 0)
+// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = inttoptr i64 %[[T23]] to ptr
+// CHECK: store ptr %[[SIGN_VTADDR1]], ptr %[[T20]]
diff --git a/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp
new file mode 100644
index 0000000000000..00b1cbd06e0f0
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp
@@ -0,0 +1,309 @@
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O0 -disable-llvm-passes -o - | FileCheck --check-prefix=CHECK %s
+
+// The actual vtable construction
+
+// CHECK: @_ZTV1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1C,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 4))],
+// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1C,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 10))] }, align 8
+
+// CHECK: @_ZTT1C = unnamed_addr constant [2 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 3), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 6), i32 2)], align 8
+
+// CHECK: @_ZTV1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1D,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 6))],
+// CHECK-SAME: [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1D,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 10))] }, align 8
+
+// CHECK: @_ZTT1D = unnamed_addr constant [2 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 3), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 6), i32 2)], align 8
+
+// CHECK: @_ZTV1F = unnamed_addr constant { [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] } { [6 x ptr] [ptr inttoptr (i64 32 to ptr), ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1F,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1FD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1FD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 5))], [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1F,
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1FD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1FD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 6))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1F,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD1EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 2043, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 63674, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 10))], [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -32 to ptr), ptr @_ZTI1F,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1fEv, i32 0, i64 28408, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1gEv, i32 0, i64 22926, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1hEz, i32 0, i64 9832, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD1Ev, i32 0, i64 5817, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1FD0Ev, i32 0, i64 26464, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 10))] }, align 8
+
+// CHECK: @_ZTT1F = unnamed_addr constant [8 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-32, 16) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 4), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 6), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 3), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 6), i32 2)], align 8
+
+// CHECK: @_ZTV1G = unnamed_addr constant { [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] } { [6 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 24 to ptr), ptr null, ptr @_ZTI1G,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1GD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1GD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 5))], [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1G,
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1GD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZThn8_N1GD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 6))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1G,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1fEv, i32 0, i64 28408, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1gEv, i32 0, i64 22926, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1hEz, i32 0, i64 9832, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD1Ev, i32 0, i64 5817, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD0Ev, i32 0, i64 26464, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 10))], [11 x ptr] [ptr inttoptr (i64 -24 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -24 to ptr), ptr @_ZTI1G,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD1EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 2043, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 63674, ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 10))] }, align 8
+
+// CHECK: @_ZTT1G = unnamed_addr constant [8 x ptr] [ptr ptrauth (ptr getelementptr inbounds inrange(-32, 16) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 4), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 6), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 6), i32 2),
+// CHECK-SAME: ptr ptrauth (ptr getelementptr inbounds inrange(-24, 32) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 3), i32 2)], align 8
+
+// CHECK: @_ZTV1A = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1A,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1AD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1AD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 6))] }, align 8
+
+// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr]
+
+// CHECK: @_ZTS1A = constant [3 x i8] c"1A\00", align 1
+
+// CHECK: @_ZTI1A = constant { ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2), ptr @_ZTS1A }, align 8
+
+// CHECK: @_ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr]
+
+// CHECK: @_ZTS1C = constant [3 x i8] c"1C\00", align 1
+
+// CHECK: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global [0 x ptr]
+
+// CHECK: @_ZTS1B = linkonce_odr hidden constant [3 x i8] c"1B\00", align 1
+
+// CHECK: @_ZTI1B = linkonce_odr hidden constant { ptr, ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), i32 2), ptr inttoptr (i64 add (i64 ptrtoint (ptr @_ZTS1B to i64), i64 -9223372036854775808) to ptr), ptr @_ZTI1A }, align 8
+
+// CHECK: @_ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1C, i32 0, i32 1, ptr @_ZTI1B, i64 -6141 }, align 8
+
+// CHECK: @_ZTS1D = constant [3 x i8] c"1D\00", align 1
+
+// CHECK: @_ZTI1D = constant { ptr, ptr, i32, i32, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1D, i32 0, i32 1, ptr @_ZTI1B, i64 -6141 }, align 8
+
+// CHECK: @_ZTV1E = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1E,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1fEv, i32 0, i64 28408, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1gEv, i32 0, i64 22926, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1E1hEz, i32 0, i64 9832, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1ED1Ev, i32 0, i64 5817, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1ED0Ev, i32 0, i64 26464, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 6))] }, align 8
+
+// CHECK: @_ZTS1E = constant [3 x i8] c"1E\00", align 1
+
+// CHECK: @_ZTI1E = constant { ptr, ptr } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2), ptr @_ZTS1E }, align 8
+
+// CHECK: @_ZTC1F0_1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1C,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 4))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1C,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 10))] }, align 8
+
+// CHECK: @_ZTC1F8_1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1D,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 6))], [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1D,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 10))] }, align 8
+
+// CHECK: @_ZTS1F = constant [3 x i8] c"1F\00", align 1
+
+// CHECK: @_ZTI1F = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1F, i32 3, i32 3, ptr @_ZTI1C, i64 2, ptr @_ZTI1D, i64 2050, ptr @_ZTI1E, i64 -8189 }, align 8
+
+// CHECK: @_ZTC1G0_1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 24 to ptr), ptr null, ptr @_ZTI1C,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD1Ev, i32 0, i64 31214, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1CD0Ev, i32 0, i64 8507, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 4))], [11 x ptr] [ptr inttoptr (i64 -24 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -24 to ptr), ptr @_ZTI1C,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 10))] }, align 8
+
+// CHECK: @_ZTC1G8_1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1D,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD1Ev, i32 0, i64 59423, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1DD0Ev, i32 0, i64 25900, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1gEv, i32 0, i64 59070, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1D1hEz, i32 0, i64 65100, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 6))], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1D,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 7)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 8)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 9)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 10))] }, align 8
+
+// CHECK: @_ZTS1G = constant [3 x i8] c"1G\00", align 1
+
+// CHECK: @_ZTI1G = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64, ptr, i64 } { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2), ptr @_ZTS1G, i32 3, i32 3, ptr @_ZTI1E, i64 -8189, ptr @_ZTI1C, i64 2, ptr @_ZTI1D, i64 2050 }, align 8
+
+// CHECK: @_ZTV1B = linkonce_odr unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1B,
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1fEv, i32 0, i64 55636, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 2)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1gEv, i32 0, i64 19402, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1A1hEz, i32 0, i64 31735, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 4)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1BD1Ev, i32 0, i64 2043, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 5)),
+// CHECK-SAME: ptr ptrauth (ptr @_ZN1BD0Ev, i32 0, i64 63674, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 6))] }, align 8
+
+extern "C" int printf(const char *format, ...);
+
+class A {
+public:
+  A() {}
+  virtual int f();
+  virtual int g();
+  virtual int h(...);
+  virtual ~A() {}
+
+public:
+  bool necessary_field;
+};
+
+class B : public A {
+public:
+  B() : A() {}
+  virtual ~B() {}
+};
+
+class C : public virtual B {
+public:
+  C() : B() {}
+  ~C();
+};
+
+class D : public virtual B {
+public:
+  D() : B() {}
+  ~D();
+  virtual int g();
+  virtual int h(...);
+};
+
+class E {
+public:
+  virtual int f();
+  virtual int g();
+  virtual int h(...);
+  virtual ~E(){};
+};
+
+class F : public C, public D, public virtual E {
+  ~F();
+};
+
+class G : public virtual E, public C, public D {
+  ~G();
+};
+
+C::~C() {}
+D::~D() {}
+F::~F() {}
+G::~G() {}
+int E::f() { return 1; }
+int A::f() { return 0; }
+int E::g() { return 1; }
+int A::g() { return 0; }
+int D::g() { return 0; }
+
+int E::h(...) { return 1; }
+int A::h(...) { return 0; }
+int D::h(...) { return 0; }
+
+int main() {
+  A *ans = new C();
+  delete ans;
+
+  B *b = new D();
+  b->f();
+  b->g();
+  b->h(1,2,3);
+  b = new C();
+  b->f();
+  b->h(1,2,3);
+  b = new C();
+  b->f();
+  b->h(1,2,3);
+  b = new F();
+  b->f();
+  b->g();
+  b->h(1,2,3);
+
+  ans = new B();
+  delete ans;
+
+  ans = new F();
+  ans->f();
+  ans->g();
+  ans->h(1,2,3);
+  delete ans;
+
+  E *e = new F();
+  e->f();
+  e->g();
+  e->h(1,2,3);
+  delete e;
+  e = new G();
+  e->f();
+  e->g();
+  e->h(1,2,3);
+  delete e;
+}
+
+// And check the thunks
+// CHECK: ptr @_ZTv0_n48_N1CD1Ev(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866)
+
+// CHECK: void @_ZTv0_n48_N1CD0Ev(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866)
+
+// CHECK: ptr @_ZTv0_n48_N1DD1Ev(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866)
+
+// CHECK: void @_ZTv0_n48_N1DD0Ev(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866)
+
+// CHECK: void @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866)
+
+// CHECK: void @_ZTv0_n48_N1FD0Ev(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 12810)
+
+// CHECK: void @_ZTv0_n48_N1GD0Ev(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 12810)
+
+// CHECK: void @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E(ptr noundef %this)
+// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866)
diff --git a/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp b/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
index c70316214cf66..bb52b9d23f46c 100644
--- a/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
+++ b/clang/test/CodeGenCXX/ubsan-vtable-checks.cpp
@@ -2,6 +2,7 @@
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null %s -o - | FileCheck %s --check-prefix=CHECK-NULL --check-prefix=MSABI
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=ITANIUM
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=MSABI  --check-prefix=CHECK-VPTR-MS
+// RUN: %clang_cc1 -std=c++11 -triple arm64e-ios-13 -emit-llvm -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK-VPTR --check-prefix=ITANIUM --check-prefix=CHECK-PTRAUTH
 struct T {
   virtual ~T() {}
   virtual int v() { return 1; }
@@ -26,18 +27,49 @@ int get_v(T* t) {
   // CHECK-NULL: call void @__ubsan_handle_type_mismatch_v1_abort
   // Second, we check that vtable is actually loaded once the type check is done.
   // CHECK-NULL: load ptr, ptr {{.*}}
+
+  // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable to i64
+  // CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0), !nosanitize !2
+  // CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr
+  // CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64
+  // Make sure authed vtable pointer feeds into hashing
+  // CHECK-PTRAUTH: {{%.*}} = xor i64 {{.*}}, [[STRIPPED_INT]]
+
+  // Verify that we authenticate for the actual vcall
+  // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 17113)
+  // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable2 to i64
+  // CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]])
+  // CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr
+  // CHECK-PTRAUTH: {{%.*}} = getelementptr inbounds ptr, ptr [[AUTHED_PTR]], i64 2
   return t->v();
 }
 
 // ITANIUM: define{{.*}} void @_Z9delete_itP1T
 // MSABI: define dso_local void @"?delete_it
 void delete_it(T *t) {
-  // First, we check that vtable is not loaded before a type check.
   // CHECK-VPTR-NOT: load {{.*}} (ptr{{.*}})**, {{.*}} (ptr{{.*}})***
   // CHECK-VPTR: br i1 {{.*}} label %{{.*}}
-  // CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort
+  // CHECK-VPTR: call void @__ubsan_handle_type_mismatch_v1_abort
   // Second, we check that vtable is actually loaded once the type check is done.
   // CHECK-VPTR: load ptr, ptr {{.*}}
+
+  // First, we check that vtable is not loaded before a type check.
+  // CHECK-PTRAUTH: ptrtoint ptr {{%.*}} to i64
+  // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr [[VTABLE:%.*]] to i64
+  // CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0)
+  // CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr
+  // CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64
+  // CHECK-PTRAUTH: {{%.*}} = xor i64 {{.*}}, [[STRIPPED_INT]]
+  // CHECK-PTRAUTH: call void @__ubsan_handle_dynamic_type_cache_miss_abort(
+  // Second, we check that vtable is actually loaded once the type check is done.
+  // ptrauth for the virtual function load
+  // CHECK-PTRAUTH: [[VTABLE2:%.*]] = load ptr, ptr {{.*}}
+  // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 %{{.*}}, i64 17113)
+  // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr [[VTABLE2]] to i64
+  // CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]])
+  // CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr
+  // CHECK-PTRAUTH: getelementptr inbounds ptr, ptr
+  // CHECK-PTRAUTH {{%.*}} = getelementptr inbounds ptr, ptr [[AUTHED_PTR]], i64 1
   delete t;
 }
 
@@ -47,7 +79,19 @@ U* dyncast(T *t) {
   // First, we check that dynamic_cast is not called before a type check.
   // CHECK-VPTR-NOT: call ptr @__{{dynamic_cast|RTDynamicCast}}
   // CHECK-VPTR: br i1 {{.*}} label %{{.*}}
+  // CHECK-PTRAUTH: [[V0:%.*]] = ptrtoint ptr {{%.*}} to i64
+  // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[V0]], i64 17113)
+  // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr {{%.*}} to i64
+  // CHECK-PTRAUTH: [[STRIPPED_VTABLE:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[CAST_VTABLE]], i32 0)
+  // CHECK-PTRAUTH: [[STRIPPED_PTR:%.*]] = inttoptr i64 [[STRIPPED_VTABLE]] to ptr
+  // CHECK-PTRAUTH: [[STRIPPED_INT:%.*]] = ptrtoint ptr [[STRIPPED_PTR]] to i64
+  // CHECK-PTRAUTH: {{%.*}} = xor i64 {{.*}}, [[STRIPPED_INT]]
   // CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort
+  // CHECK-PTRAUTH: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 17113)
+  // CHECK-PTRAUTH: [[CAST_VTABLE:%.*]] = ptrtoint ptr %vtable1 to i64
+  // CHECK-PTRAUTH: [[AUTHED_INT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VTABLE]], i32 2, i64 [[BLENDED]])
+  // CHECK-PTRAUTH: [[AUTHED_PTR:%.*]] = inttoptr i64 [[AUTHED_INT]] to ptr
+  // CHECK-PTRAUTH: {{%.*}} = load volatile i8, ptr [[AUTHED_PTR]], align 8
   // Second, we check that dynamic_cast is actually called once the type check is done.
   // CHECK-VPTR: call ptr @__{{dynamic_cast|RTDynamicCast}}
   return dynamic_cast<U*>(t);
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 99732694f72a5..28df04c5e33ef 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -201,6 +201,7 @@
 // CHECK-NEXT: Uninitialized (SubjectMatchRule_variable_is_local)
 // CHECK-NEXT: UnsafeBufferUsage (SubjectMatchRule_function)
 // CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: VTablePointerAuthentication (SubjectMatchRule_record)
 // CHECK-NEXT: VecReturn (SubjectMatchRule_record)
 // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
 // CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
diff --git a/clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp b/clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp
new file mode 100644
index 0000000000000..41bbba0e832fd
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -Wno-vla -fsyntax-only -verify -fptrauth-intrinsics -fptrauth-calls %s
+
+struct Incomplete0; // expected-note 3 {{forward declaration of 'Incomplete0'}}
+
+template <class T>
+struct Incomplete1; // expected-note {{template is declared here}}
+
+struct Complete0 {
+};
+
+template <class T>
+struct Complete1 {
+};
+
+struct S {
+  virtual int foo();
+  virtual Incomplete0 virtual0(); // expected-note 2 {{'Incomplete0' is incomplete}}
+  virtual void virtual1(Incomplete1<int>); // expected-note {{'Incomplete1<int>' is incomplete}}
+  virtual Complete0 virtual2();
+  virtual Complete1<int> virtual3();
+  Incomplete0 nonvirtual0();
+  template <class T>
+  void m0() {
+    (void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}}
+  }
+};
+
+template <bool T>
+struct S2 {
+  virtual Incomplete0 virtual0() noexcept(T); // expected-note {{'Incomplete0' is incomplete}}
+
+  void m0() {
+    (void)&S2<T>::virtual0;
+  }
+
+  void m1() {
+    (void)&S2<T>::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}}
+  }
+};
+
+void test_incomplete_virtual_member_function_return_arg_type() {
+  (void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0}} expected-note {{cannot take an address of a virtual member function}}
+  (void)&S::virtual1; // expected-error {{implicit instantiation of undefined template 'Incomplete1<int>'}} expected-note {{cannot take an address of a virtual member function}}
+  (void)&S::virtual2;
+  (void)&S::virtual3;
+  (void)&S::nonvirtual0;
+  int s = sizeof(&S::virtual0);
+  S2<true>().m1(); // expected-note {{in instantiation of}}
+}
+
diff --git a/clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp b/clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp
new file mode 100644
index 0000000000000..3a3386196fbfa
--- /dev/null
+++ b/clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp
@@ -0,0 +1,225 @@
+// RUN: %clang_cc1 -fsyntax-only -triple arm64-apple-ios -verify -fptrauth-calls -std=c++2a %s
+
+namespace basic {
+
+#define authenticated(a, b, c...) [[clang::ptrauth_vtable_pointer(a, b, c)]]
+
+// Basic sanity tests
+#define TEST_AUTH(name, auth...)                        \
+  struct [[clang::ptrauth_vtable_pointer(auth)]] name { \
+    virtual ~name() {}                                  \
+  }
+
+TEST_AUTH(NoParams);
+// expected-error at -1{{'ptrauth_vtable_pointer' attribute takes at least 3 arguments}}
+TEST_AUTH(NoAuth, no_authentication, default_address_discrimination, default_extra_discrimination);
+TEST_AUTH(InvalidKey, wat, default_address_discrimination, default_extra_discrimination);
+// expected-error at -1{{invalid authentication key 'wat'}}
+TEST_AUTH(InvalidAddressDiscrimination, no_authentication, wat, default_extra_discrimination);
+// expected-error at -1{{invalid address discrimination mode 'wat'}}
+TEST_AUTH(InvalidExtraDiscrimination, no_authentication, default_address_discrimination, wat);
+// expected-error at -1{{invalid extra discrimination selection 'wat'}}
+TEST_AUTH(InvalidNoCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination);
+// expected-error at -1{{missing custom discrimination}}
+TEST_AUTH(InvalidCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination, wat);
+// expected-error at -1{{invalid custom discrimination}}
+TEST_AUTH(Default, default_key, default_address_discrimination, default_extra_discrimination);
+TEST_AUTH(InvalidDefaultExtra, default_key, default_address_discrimination, default_extra_discrimination, 1);
+// expected-error at -1{{'ptrauth_vtable_pointer' attribute takes no more than 3 arguments}}
+TEST_AUTH(ProcessDependentKey, process_dependent, default_address_discrimination, default_extra_discrimination);
+TEST_AUTH(ProcessIndependentKey, process_independent, default_address_discrimination, default_extra_discrimination);
+TEST_AUTH(DefaultAddressDiscrimination, process_independent, default_address_discrimination, default_extra_discrimination);
+TEST_AUTH(NoAddressDiscrimination, process_independent, no_address_discrimination, default_extra_discrimination);
+TEST_AUTH(AddressDiscrimination, process_independent, address_discrimination, default_extra_discrimination);
+TEST_AUTH(DefaultExtraDiscrimination, process_independent, default_address_discrimination, default_extra_discrimination);
+TEST_AUTH(NoExtraDiscrimination, process_independent, default_address_discrimination, no_extra_discrimination);
+TEST_AUTH(TypeExtraDiscrimination, process_independent, default_address_discrimination, type_discrimination);
+TEST_AUTH(InvalidCustomExtraDiscrimination, process_independent, default_address_discrimination, custom_discrimination);
+// expected-error at -1{{missing custom discrimination}}
+TEST_AUTH(ValidCustomExtraDiscrimination, process_independent, default_address_discrimination, custom_discrimination, 1);
+
+// Basic valid authentication configuration
+#define generic_authenticated \
+  authenticated(process_independent, address_discrimination, type_discrimination)
+
+struct generic_authenticated ForwardDecl;
+
+struct generic_authenticated generic_authenticated InvalidDuplicateAttribute {
+  // expected-error at -1{{multiple vtable pointer authentication policies on 'InvalidDuplicateAttribute'}}
+  virtual ~InvalidDuplicateAttribute(){};
+};
+struct generic_authenticated ValidPolymorphic {
+  virtual ~ValidPolymorphic(){};
+};
+struct generic_authenticated InvalidMonomorphic { // expected-error{{cannot set vtable pointer authentication on monomorphic type 'InvalidMonomorphic'}}
+};
+struct ValidMonomorphic {
+};
+
+struct ValidSubclass : ValidPolymorphic {};
+struct generic_authenticated InvalidSubclass : ValidPolymorphic {}; // expected-error{{cannot set vtable pointer authentication on 'InvalidSubclass' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+
+// Awful template time
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedMonomorphicTemplateClass : T {};
+// expected-error at -1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidPolymorphic>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+// expected-error at -2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidMonomorphic>'}}
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedPolymorphicTemplateClass : T { // expected-error{{cannot set vtable pointer authentication on 'ExplicitlyAuthedPolymorphicTemplateClass<basic::ValidPolymorphic>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+  virtual ~ExplicitlyAuthedPolymorphicTemplateClass(){};
+};
+template <typename T>
+struct UnauthedMonomorphicTemplateClass : T {};
+template <typename T>
+struct UnauthedPolymorphicTemplateClass : T {
+  virtual ~UnauthedPolymorphicTemplateClass(){};
+};
+
+ExplicitlyAuthedMonomorphicTemplateClass<ValidPolymorphic> test1;
+// expected-note at -1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidPolymorphic>' requested here}}
+ExplicitlyAuthedMonomorphicTemplateClass<ValidMonomorphic> test2;
+// expected-note at -1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidMonomorphic>' requested here}}
+ExplicitlyAuthedPolymorphicTemplateClass<ValidPolymorphic> test3;
+// expected-note at -1{{in instantiation of template class 'basic::ExplicitlyAuthedPolymorphicTemplateClass<basic::ValidPolymorphic>' requested here}}
+ExplicitlyAuthedPolymorphicTemplateClass<ValidMonomorphic> test4;
+
+UnauthedMonomorphicTemplateClass<ValidPolymorphic> test5;
+UnauthedMonomorphicTemplateClass<ValidMonomorphic> test6;
+UnauthedPolymorphicTemplateClass<ValidPolymorphic> test7;
+UnauthedPolymorphicTemplateClass<ValidMonomorphic> test8;
+
+// Just use a different policy from the generic macro to verify we won't complain
+// about the insanity
+struct authenticated(process_independent, no_address_discrimination, type_discrimination) SecondAuthenticatedPolymorphic {
+  virtual ~SecondAuthenticatedPolymorphic(){};
+};
+struct UnauthenticatedPolymorphic {
+  virtual ~UnauthenticatedPolymorphic(){};
+};
+
+struct MultipleParents1 : ValidPolymorphic, SecondAuthenticatedPolymorphic, UnauthenticatedPolymorphic {};
+struct MultipleParents2 : UnauthenticatedPolymorphic, ValidPolymorphic, SecondAuthenticatedPolymorphic {};
+struct generic_authenticated InvalidMultipleParents : UnauthenticatedPolymorphic, ValidPolymorphic, SecondAuthenticatedPolymorphic {};
+// expected-error at -1{{cannot set vtable pointer authentication on 'InvalidMultipleParents' which is a subclass of polymorphic type 'UnauthenticatedPolymorphic'}}
+
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedPolymorphicTemplateClassNoBase {
+  virtual ~ExplicitlyAuthedPolymorphicTemplateClassNoBase();
+};
+
+ExplicitlyAuthedPolymorphicTemplateClassNoBase<int> v;
+
+struct ValidSubclassOfTemplate : ExplicitlyAuthedPolymorphicTemplateClassNoBase<int> {
+};
+
+struct generic_authenticated InvalidSubclassOfTemplate : ExplicitlyAuthedPolymorphicTemplateClassNoBase<int> {
+  // expected-error at -1{{cannot set vtable pointer authentication on 'InvalidSubclassOfTemplate' which is a subclass of polymorphic type 'ExplicitlyAuthedPolymorphicTemplateClassNoBase<int>'}}
+};
+
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedMonomorphicTemplateClassNoBase {
+  // expected-error at -1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase'}}
+  // expected-error at -2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase<int>'}}
+};
+
+ExplicitlyAuthedMonomorphicTemplateClassNoBase<int> X;
+// expected-note at -1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClassNoBase<int>' requested here}}
+
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedTemplateClassValidBase : ValidMonomorphic {
+  // expected-error at -1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase'}}
+  // expected-error at -2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase<int>'}}
+};
+
+ExplicitlyAuthedTemplateClassValidBase<int> Y;
+// expected-note at -1{{in instantiation of template class 'basic::ExplicitlyAuthedTemplateClassValidBase<int>' requested here}}
+
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedTemplateClassInvalidBase : ValidPolymorphic {
+  // expected-error at -1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+  // expected-error at -2{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase<int>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+};
+
+ExplicitlyAuthedTemplateClassInvalidBase<int> Z;
+// expected-note at -1{{in instantiation of template class 'basic::ExplicitlyAuthedTemplateClassInvalidBase<int>' requested here}}
+
+template <class test1, class test2>
+class generic_authenticated TestPolymorphicTemplateSpecialization;
+
+template <>
+class TestPolymorphicTemplateSpecialization<double, float> {
+  MissingDecl *zl;
+  // expected-error at -1 {{unknown type name 'MissingDecl'}}
+public:
+  virtual ~TestPolymorphicTemplateSpecialization();
+};
+template <class test1>
+class generic_authenticated TestPolymorphicTemplateSpecialization<test1, double>
+// expected-error at -1 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization<test1, double>'}}
+// expected-error at -2 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization<double, double>'}}
+{
+};
+
+TestPolymorphicTemplateSpecialization<double, float> b;
+TestPolymorphicTemplateSpecialization<double, double> b2;
+// expected-note at -1 {{in instantiation of template class 'basic::TestPolymorphicTemplateSpecialization<double, double>' requested here}}
+
+template <typename A> class generic_authenticated TestMonomorphic {};
+// expected-error at -1 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphic'}}
+// expected-error at -2 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphic<double>'}}
+
+template <> class generic_authenticated TestMonomorphic<int> {
+public:
+  virtual ~TestMonomorphic();
+};
+
+struct TestMonomorphicSubclass : TestMonomorphic<int> {
+};
+template <typename T> struct generic_authenticated TestMonomorphicSubclass2 : TestMonomorphic<T> {
+  // expected-error at -1 {{cannot set vtable pointer authentication on 'TestMonomorphicSubclass2<int>' which is a subclass of polymorphic type 'TestMonomorphic<int>'}}
+  // expected-error at -2 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphicSubclass2<double>'}}
+  // expected-note at -3 {{in instantiation of template class 'basic::TestMonomorphic<double>' requested here}}
+};
+
+TestMonomorphicSubclass tms_1;
+TestMonomorphicSubclass2<int> tms2_1;
+// expected-note at -1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<int>' requested here}}
+TestMonomorphicSubclass2<double> tms2_2;
+// expected-note at -1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<double>' requested here}}
+// expected-note at -2 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<double>' requested here}}
+
+template <typename T>
+class generic_authenticated dependent_type {
+  // expected-error at -1 {{cannot set vtable pointer authentication on monomorphic type 'dependent_type'}}
+  static constexpr unsigned small_object_size = 1;
+  char _model[small_object_size];
+};
+
+template <typename... T>
+class generic_authenticated dependent_type2 : public T... {
+  // expected-error at -1 {{cannot set vtable pointer authentication on 'dependent_type2<basic::Foo>' which is a subclass of polymorphic type 'Foo'}}
+  static constexpr unsigned small_object_size = 1;
+  char _model[small_object_size];
+};
+
+struct Foo {
+  virtual ~Foo();
+};
+
+dependent_type2<Foo> thing;
+// expected-note at -1 {{in instantiation of template class 'basic::dependent_type2<basic::Foo>' requested here}}
+
+template <class>
+class task;
+template <unsigned align> struct alignedthing {
+  char buffer[align];
+};
+
+template <class R, class... Args>
+class generic_authenticated task<R(Args...)> {
+  // expected-error at -1 {{cannot set vtable pointer authentication on monomorphic type 'task<R (Args...)>'}}
+  static constexpr __SIZE_TYPE__ small_object_size = 256;
+  alignedthing<small_object_size> _model;
+};
+
+} // namespace basic
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index ca7630adfbb7b..c5838b16fb087 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -2567,6 +2567,32 @@ static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &O
   OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n";
 }
 
+// Emits the indexed-argument-is-identifier property for attributes.
+static void emitClangAttrStrictIdentifierArgAtIndexList(RecordKeeper &Records,
+                                                        raw_ostream &OS) {
+  OS << "#if defined(CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST)\n";
+  std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
+
+  for (const auto *Attr : Attrs) {
+    if (!Attr->getValueAsBit("StrictEnumParameters"))
+      continue;
+    // Determine whether the first argument is an identifier.
+    std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
+    uint64_t enumAtIndex = 0;
+    for (size_t i = 0; i < Args.size(); i++) {
+      enumAtIndex |= ((uint64_t)isIdentifierArgument(Args[0])) << i;
+    }
+    if (!enumAtIndex)
+      continue;
+
+    // All these spellings take an identifier argument.
+    forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
+      OS << ".Case(\"" << S.name() << "\", " << enumAtIndex << "ull)\n";
+    });
+  }
+  OS << "#endif // CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST\n\n";
+}
+
 static bool keywordThisIsaIdentifierInArgument(const Record *Arg) {
   return !Arg->getSuperClasses().empty() &&
          llvm::StringSwitch<bool>(
@@ -4948,6 +4974,7 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS) {
   emitClangAttrTypeArgList(Records, OS);
   emitClangAttrLateParsedList(Records, OS);
   emitClangAttrLateParsedExperimentalList(Records, OS);
+  emitClangAttrStrictIdentifierArgAtIndexList(Records, OS);
 }
 
 void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,



More information about the llvm-branch-commits mailing list