[llvm-branch-commits] [clang] [clang] Implement pointer authentication for C++ virtual functions, v-tables, and VTTs (PR #94054)
Oliver Hunt via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri May 31 15:05:15 PDT 2024
https://github.com/ojhunt created https://github.com/llvm/llvm-project/pull/94054
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 3523eaf73c2df05b7b615eca9d84b7ec9eb3c31f 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..c0c4442c878d1 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) || !checkUInt32Argument(S, 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