aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/ASTContext.h15
-rw-r--r--clang/include/clang/AST/GlobalDecl.h4
-rw-r--r--clang/include/clang/AST/Mangle.h11
-rw-r--r--clang/include/clang/AST/VTableBuilder.h29
-rw-r--r--clang/include/clang/Basic/Attr.td29
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td31
-rw-r--r--clang/include/clang/Basic/PointerAuthOptions.h25
-rw-r--r--clang/include/clang/Basic/Thunk.h14
-rw-r--r--clang/include/clang/CodeGen/CodeGenABITypes.h4
-rw-r--r--clang/include/clang/CodeGen/ConstantInitBuilder.h10
-rw-r--r--clang/include/clang/InstallAPI/Visitor.h4
-rw-r--r--clang/include/clang/Sema/Sema.h4
-rw-r--r--clang/lib/AST/ASTContext.cpp83
-rw-r--r--clang/lib/AST/ItaniumMangle.cpp90
-rw-r--r--clang/lib/AST/Mangle.cpp23
-rw-r--r--clang/lib/AST/MicrosoftMangle.cpp22
-rw-r--r--clang/lib/AST/VTableBuilder.cpp105
-rw-r--r--clang/lib/CodeGen/CGCXX.cpp11
-rw-r--r--clang/lib/CodeGen/CGCXXABI.h14
-rw-r--r--clang/lib/CodeGen/CGClass.cpp30
-rw-r--r--clang/lib/CodeGen/CGExpr.cpp8
-rw-r--r--clang/lib/CodeGen/CGExprConstant.cpp46
-rw-r--r--clang/lib/CodeGen/CGPointerAuth.cpp212
-rw-r--r--clang/lib/CodeGen/CGVTT.cpp5
-rw-r--r--clang/lib/CodeGen/CGVTables.cpp48
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp63
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h27
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h29
-rw-r--r--clang/lib/CodeGen/ConstantEmitter.h3
-rw-r--r--clang/lib/CodeGen/ConstantInitBuilder.cpp18
-rw-r--r--clang/lib/CodeGen/ItaniumCXXABI.cpp77
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp18
-rw-r--r--clang/lib/Frontend/CompilerInvocation.cpp11
-rw-r--r--clang/lib/Headers/ptrauth.h14
-rw-r--r--clang/lib/InstallAPI/Visitor.cpp13
-rw-r--r--clang/lib/Parse/ParseDecl.cpp30
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp114
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp37
-rw-r--r--clang/lib/Sema/SemaExpr.cpp33
-rw-r--r--clang/test/CodeGen/ptrauth-ubsan-vptr.cpp30
-rw-r--r--clang/test/CodeGenCXX/catch-undef-behavior.cpp7
-rw-r--r--clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp105
-rw-r--r--clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp42
-rw-r--r--clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp50
-rw-r--r--clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp564
-rw-r--r--clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp10
-rw-r--r--clang/test/CodeGenCXX/ptrauth-thunks.cpp28
-rw-r--r--clang/test/CodeGenCXX/ptrauth-virtual-function.cpp581
-rw-r--r--clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp309
-rw-r--r--clang/test/CodeGenCXX/ubsan-vtable-checks.cpp48
-rw-r--r--clang/test/Misc/pragma-attribute-supported-attributes-list.test1
-rw-r--r--clang/test/SemaCXX/ptrauth-incomplete-virtual-member-function-return-arg-type.cpp50
-rw-r--r--clang/test/SemaCXX/vtable_pointer_authentication_attribute.cpp225
-rw-r--r--clang/utils/TableGen/ClangAttrEmitter.cpp27
54 files changed, 3343 insertions, 98 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 7aa1357..a99f2dc 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>
@@ -1277,6 +1278,11 @@ public:
/// space.
QualType removeAddrSpaceQualType(QualType T) const;
+ /// Return the "other" discriminator used for the pointer auth schema used for
+ /// vtable pointers in instances of the requested type.
+ uint16_t
+ getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD);
+
/// 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
@@ -3438,12 +3444,21 @@ public:
/// 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 *ThisClass);
+ 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/GlobalDecl.h b/clang/include/clang/AST/GlobalDecl.h
index 88abba2..386693c 100644
--- a/clang/include/clang/AST/GlobalDecl.h
+++ b/clang/include/clang/AST/GlobalDecl.h
@@ -145,6 +145,10 @@ public:
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/Mangle.h b/clang/include/clang/AST/Mangle.h
index e586b0c..d5f6c0f 100644
--- a/clang/include/clang/AST/Mangle.h
+++ b/clang/include/clang/AST/Mangle.h
@@ -130,15 +130,15 @@ public:
// 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 @@ public:
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/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h
index fbf6c04..a5de41d 100644
--- a/clang/include/clang/AST/VTableBuilder.h
+++ b/clang/include/clang/AST/VTableBuilder.h
@@ -361,6 +361,10 @@ public:
};
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 @@ private:
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 @@ public:
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/Attr.td b/clang/include/clang/Basic/Attr.td
index 0c469e3..452cd18 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -686,6 +686,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 = [];
@@ -4576,6 +4580,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 79cc9c6..33412e1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -940,6 +940,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">,
@@ -12220,6 +12227,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 aaad4a2..197d636 100644
--- a/clang/include/clang/Basic/PointerAuthOptions.h
+++ b/clang/include/clang/Basic/PointerAuthOptions.h
@@ -47,6 +47,12 @@ public:
/// 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,
};
@@ -150,6 +156,25 @@ public:
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 0247e27..af4afb2 100644
--- a/clang/include/clang/Basic/Thunk.h
+++ b/clang/include/clang/Basic/Thunk.h
@@ -162,20 +162,24 @@ 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;
+ const Type *ThisType;
- ThunkInfo() : Method(nullptr) {}
+ ThunkInfo() : Method(nullptr), ThisType(nullptr) {}
ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return,
- const CXXMethodDecl *Method = nullptr)
- : This(This), Return(Return), Method(Method) {}
+ const Type *ThisT, const CXXMethodDecl *Method = nullptr)
+ : This(This), Return(Return), Method(Method), ThisType(ThisT) {}
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 fda0855..d4822dc 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);
+
/// Given the language and code-generation options that Clang was configured
/// with, set the default LLVM IR attributes for a function definition.
/// The attributes set here are mostly global target-configuration and
diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h
index 498acfd..28d4764 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,11 @@ public:
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 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 9ac948d..3680ee5 100644
--- a/clang/include/clang/InstallAPI/Visitor.h
+++ b/clang/include/clang/InstallAPI/Visitor.h
@@ -60,8 +60,8 @@ private:
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 2e7af0f..ab3cbd1 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4566,6 +4566,10 @@ public:
/// 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 1b5d16b..84deaf5 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/AArch64TargetParser.h"
#include "llvm/TargetParser/Triple.h"
@@ -3128,6 +3129,17 @@ QualType ASTContext::removeAddrSpaceQualType(QualType T) const {
return QualType(TypeNode, Quals.getFastQualifiers());
}
+uint16_t
+ASTContext::getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD) {
+ assert(RD->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(RD, Out);
+ return llvm::getPointerAuthStableSipHash(Str);
+}
+
QualType ASTContext::getObjCGCQualType(QualType T,
Qualifiers::GC GCAttr) const {
QualType CanT = getCanonicalType(T);
@@ -13894,3 +13906,74 @@ 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 2cac03b..5444dcf 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -99,11 +99,10 @@ public:
}
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 @@ public:
void mangleNameOrStandardSubstitution(const NamedDecl *ND);
void mangleLambdaSig(const CXXRecordDecl *Lambda);
void mangleModuleNamePrefix(StringRef Name, bool IsPartition = false);
+ void mangleVendorQualifier(StringRef Name);
private:
@@ -559,7 +559,6 @@ private:
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);
@@ -7044,8 +7043,78 @@ void ItaniumMangleContextImpl::mangleCXXDtorComdat(const CXXDestructorDecl *D,
Mangler.mangle(GlobalDecl(D, Dtor_Comdat));
}
+/// Mangles the pointer authentication override attribute for classes
+/// that have explicit overrides for the vtable authentication schema.
+///
+/// The override is mangled as a parameterized vendor extension as follows
+///
+/// <type> ::= U "__vtptrauth" I
+/// <key>
+/// <addressDiscriminated>
+/// <extraDiscriminator>
+/// E
+///
+/// The extra discriminator encodes the explicit value derived from the
+/// override schema, e.g. if the override has specified type based
+/// discrimination the encoded value will be the discriminator derived from the
+/// type name.
+static void mangleOverrideDiscrimination(CXXNameMangler &Mangler,
+ ASTContext &Context,
+ const ThunkInfo &Thunk) {
+ auto &LangOpts = Context.getLangOpts();
+ const CXXRecordDecl *ThisRD = Thunk.ThisType->getPointeeCXXRecordDecl();
+ const CXXRecordDecl *PtrauthClassRD =
+ Context.baseForVTableAuthentication(ThisRD);
+ unsigned TypedDiscriminator =
+ Context.getPointerAuthVTablePointerDiscriminator(ThisRD);
+ Mangler.mangleVendorQualifier("__vtptrauth");
+ auto &ManglerStream = Mangler.getStream();
+ ManglerStream << "I";
+ if (const auto *ExplicitAuth =
+ PtrauthClassRD->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
@@ -7071,21 +7140,28 @@ 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 4fbf0e3..75f6e21 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -513,10 +513,20 @@ public:
}
} 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;
@@ -569,11 +579,12 @@ private:
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 8cbaad6..7f1e9ab 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -159,9 +159,9 @@ public:
const MethodVFTableLocation &ML,
raw_ostream &Out) override;
void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
- raw_ostream &) override;
+ bool ElideOverrideInfo, raw_ostream &) override;
void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type,
- const ThisAdjustment &ThisAdjustment,
+ const ThunkInfo &Thunk, bool ElideOverrideInfo,
raw_ostream &) override;
void mangleCXXVFTable(const CXXRecordDecl *Derived,
ArrayRef<const CXXRecordDecl *> BasePath,
@@ -169,6 +169,8 @@ public:
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;
@@ -3747,6 +3749,7 @@ void MicrosoftMangleContextImpl::mangleVirtualMemPtrThunk(
void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
+ bool /*ElideOverrideInfo*/,
raw_ostream &Out) {
msvc_hashing_ostream MHO(Out);
MicrosoftCXXNameMangler Mangler(*this, MHO);
@@ -3768,9 +3771,11 @@ 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 /*ElideOverrideInfo*/,
+ 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.
@@ -3779,6 +3784,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);
}
@@ -3803,6 +3809,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 a956ca5..e941c3b 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -1147,11 +1147,41 @@ 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)) {
+ const CXXMethodDecl *Method = VTables.findOriginalMethodInMap(MD);
+ VTableThunks[Idx].Method = Method;
+ VTableThunks[Idx].ThisType = Method->getThisType().getTypePtr();
+ }
+ 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 +1539,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) &&
@@ -1547,7 +1579,8 @@ void ItaniumVTableBuilder::AddMethods(
// This is a virtual thunk for the most derived class, add it.
AddThunk(Overrider.Method,
- ThunkInfo(ThisAdjustment, ReturnAdjustment));
+ ThunkInfo(ThisAdjustment, ReturnAdjustment,
+ OverriddenMD->getThisType().getTypePtr()));
}
}
@@ -1615,6 +1648,15 @@ 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;
+ VTableThunks[Components.size()].ThisType = MD->getThisType().getTypePtr();
+ }
+
AddMethod(Overrider.Method, ReturnAdjustment);
}
}
@@ -1890,11 +1932,31 @@ void ItaniumVTableBuilder::LayoutVTablesForVirtualBases(
}
}
+static void printThunkMethod(const ThunkInfo &Info, raw_ostream &Out) {
+ if (!Info.Method)
+ return;
+ 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 +2040,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
}
Out << ']';
+ printThunkMethod(Thunk, Out);
}
// If this function pointer has a 'this' pointer adjustment, dump it.
@@ -1991,6 +2054,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
}
Out << ']';
+ printThunkMethod(Thunk, Out);
}
}
@@ -2027,6 +2091,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
Out << ']';
}
+ printThunkMethod(Thunk, Out);
}
break;
@@ -2125,7 +2190,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 +2378,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 CXXMethodDecl *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>
@@ -3094,9 +3187,9 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
ReturnAdjustmentOffset.VirtualBase);
}
}
-
+ auto ThisType = (OverriddenMD ? OverriddenMD : MD)->getThisType().getTypePtr();
AddMethod(FinalOverriderMD,
- ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
+ ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, ThisType,
ForceReturnAdjustmentMangling ? MD : nullptr));
}
}
diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp
index e95a735f..23ebbee 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) {
+ GlobalDecl 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 104a20d..7dcc539 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -504,13 +504,15 @@ public:
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 5a032bd..0a595bb 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -2588,6 +2588,11 @@ 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 +2686,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 534f46d..23e5dee 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -830,8 +830,14 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,
// Load the vptr, and mix it with TypeHash.
llvm::Value *TypeHash =
llvm::ConstantInt::get(Int64Ty, xxh3_64bits(Out.str()));
+
+ llvm::Type *VPtrTy = llvm::PointerType::get(IntPtrTy, 0);
Address VPtrAddr(Ptr, IntPtrTy, getPointerAlign());
- llvm::Value *VPtrVal = Builder.CreateLoad(VPtrAddr);
+ llvm::Value *VPtrVal = GetVTablePtr(VPtrAddr, VPtrTy,
+ Ty->getAsCXXRecordDecl(),
+ VTableAuthMode::UnsafeUbsanStrip);
+ VPtrVal = Builder.CreateBitOrPointerCast(VPtrVal, IntPtrTy);
+
llvm::Value *Hash =
emitHashMix(Builder, TypeHash, Builder.CreateZExt(VPtrVal, Int64Ty));
Hash = Builder.CreateTrunc(Hash, IntPtrTy);
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 0f3e297..1fec587 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -803,6 +803,13 @@ 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;
}
@@ -1647,7 +1654,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();
}
@@ -1811,6 +1818,43 @@ 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;
+
+ unsigned 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();
+ }
+
+ llvm::ConstantInt *Discriminator =
+ llvm::ConstantInt::get(CGM.IntPtrTy, Schema.getExtraDiscriminator());
+
+ llvm::Constant *SignedPointer = CGM.getConstantSignedPointer(
+ UnsignedPointer, Key, StorageAddress, Discriminator);
+
+ 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 f0819b0..673f6e6 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -11,12 +11,56 @@
//
//===----------------------------------------------------------------------===//
+#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "clang/CodeGen/CodeGenABITypes.h"
+#include "clang/CodeGen/ConstantInitBuilder.h"
+#include "llvm/Support/SipHash.h"
using namespace clang;
using namespace CodeGen;
+/// Given a pointer-authentication schema, return a concrete "other"
+/// discriminator for it.
+llvm::ConstantInt *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::getPointerAuthStableSipHash(Name);
+ }
+
+ return EntityHash;
+}
+
/// Return the abstract pointer authentication schema for a pointer to the given
/// function type.
CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) {
@@ -35,6 +79,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);
+}
+
llvm::Constant *
CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
llvm::Constant *StorageAddress,
@@ -60,6 +139,29 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
IntegerDiscriminator, AddressDiscriminator);
}
+/// Does a given PointerAuthScheme require us to sign a value
+bool CodeGenModule::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::ConstantInt *OtherDiscriminator =
+ getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType);
+
+ return getConstantSignedPointer(Pointer, Schema.getKey(), StorageAddress,
+ OtherDiscriminator);
+}
+
+/// If applicable, sign a given constant function pointer with the ABI rules for
+/// functionType.
llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer,
QualType FunctionType) {
assert(FunctionType->isFunctionType() ||
@@ -80,3 +182,113 @@ llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD,
QualType FuncType = FD->getType();
return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType);
}
+
+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 ExplicitAddressDiscrimination =
+ ExplicitAuthentication->getAddressDiscrimination();
+ auto ExplicitDiscriminator =
+ ExplicitAuthentication->getExtraDiscrimination();
+
+ unsigned ExplicitKey = ExplicitAuthentication->getKey();
+ 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 4cebb75..20bd2c2f 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 (const 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 55c3032..3e88cd7 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 (const 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 650c566..26deeca 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -3063,3 +3063,66 @@ void CodeGenFunction::EmitPointerAuthOperandBundle(
llvm::Value *Args[] = {Key, Discriminator};
Bundles.emplace_back("ptrauth", Args);
}
+
+static llvm::Value *EmitPointerAuthCommon(CodeGenFunction &CGF,
+ const CGPointerAuthInfo &PointerAuth,
+ llvm::Value *Pointer,
+ unsigned IntrinsicID) {
+ if (!PointerAuth)
+ return Pointer;
+
+ auto Key = CGF.Builder.getInt32(PointerAuth.getKey());
+
+ llvm::Value *Discriminator = PointerAuth.getDiscriminator();
+ if (!Discriminator) {
+ Discriminator = CGF.Builder.getSize(0);
+ }
+
+ // Convert the pointer to intptr_t before signing it.
+ auto OrigType = Pointer->getType();
+ Pointer = CGF.Builder.CreatePtrToInt(Pointer, CGF.IntPtrTy);
+
+ // call i64 @llvm.ptrauth.sign.i64(i64 %pointer, i32 %key, i64 %discriminator)
+ auto Intrinsic = CGF.CGM.getIntrinsic(IntrinsicID);
+ Pointer = CGF.EmitRuntimeCall(Intrinsic, {Pointer, Key, Discriminator});
+
+ // Convert back to the original type.
+ Pointer = CGF.Builder.CreateIntToPtr(Pointer, OrigType);
+ return Pointer;
+}
+
+llvm::Value *
+CodeGenFunction::EmitPointerAuthSign(const CGPointerAuthInfo &PointerAuth,
+ llvm::Value *Pointer) {
+ if (!PointerAuth.shouldSign())
+ return Pointer;
+ 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 a9c497b..13f12b5 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -2453,10 +2453,20 @@ public:
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,
@@ -4417,6 +4427,19 @@ public:
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 SchemaDecl,
+ QualType SchemaType);
+ 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 99133047..22b2b31 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -609,6 +609,13 @@ private:
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,
@@ -957,11 +964,33 @@ public:
CGPointerAuthInfo getFunctionPointerAuthInfo(QualType T);
+ bool shouldSignPointer(const PointerAuthSchema &Schema);
+ llvm::Constant *getConstantSignedPointer(llvm::Constant *Pointer,
+ const PointerAuthSchema &Schema,
+ llvm::Constant *StorageAddress,
+ GlobalDecl SchemaDecl,
+ QualType SchemaType);
+
llvm::Constant *
getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
llvm::Constant *StorageAddress,
llvm::ConstantInt *OtherDiscriminator);
+ llvm::ConstantInt *
+ 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.
bool shouldEmitRTTI(bool ForEH = false) {
return (ForEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice &&
diff --git a/clang/lib/CodeGen/ConstantEmitter.h b/clang/lib/CodeGen/ConstantEmitter.h
index a55da0d..eff0a8d 100644
--- a/clang/lib/CodeGen/ConstantEmitter.h
+++ b/clang/lib/CodeGen/ConstantEmitter.h
@@ -113,6 +113,9 @@ public:
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/ConstantInitBuilder.cpp b/clang/lib/CodeGen/ConstantInitBuilder.cpp
index 3cf69f3..549d5dd 100644
--- a/clang/lib/CodeGen/ConstantInitBuilder.cpp
+++ b/clang/lib/CodeGen/ConstantInitBuilder.cpp
@@ -296,3 +296,21 @@ ConstantAggregateBuilderBase::finishStruct(llvm::StructType *ty) {
buffer.erase(buffer.begin() + Begin, buffer.end());
return constant;
}
+
+/// 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 || !Builder.CGM.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);
+}
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 01a735c..63e36e1 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 @@ public:
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 *,
@@ -1477,10 +1480,22 @@ 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) {
+ // We perform a no-op load of the vtable pointer here to force an
+ // authentication. In environments that do not support pointer
+ // authentication this is a an actual no-op that will be elided. When
+ // pointer authentication is supported and enforced on vtable pointers this
+ // load can trap.
+ 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
@@ -1955,8 +1970,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,
@@ -2008,8 +2033,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 *
@@ -2024,7 +2050,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());
@@ -2048,7 +2074,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;
}
@@ -2147,6 +2179,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) {
@@ -2164,8 +2197,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(
@@ -2200,18 +2233,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);
}
@@ -3694,6 +3729,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 9ab634f..cc6740e 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -415,9 +415,11 @@ public:
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(
@@ -2223,9 +2225,10 @@ void MicrosoftCXXABI::emitVBTableDefinition(const VPtrInfo &VBT,
GV->setLinkage(llvm::GlobalVariable::AvailableExternallyLinkage);
}
-llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,
- Address This,
- const ThisAdjustment &TA) {
+llvm::Value *MicrosoftCXXABI::performThisAdjustment(
+ CodeGenFunction &CGF, Address This,
+ const CXXRecordDecl * /*UnadjustedClass*/, const ThunkInfo &TI) {
+ const ThisAdjustment &TA = TI.This;
if (TA.isEmpty())
return This.emitRawPointer(CGF);
@@ -2275,9 +2278,10 @@ llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,
return V;
}
-llvm::Value *
-MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, Address Ret,
- const ReturnAdjustment &RA) {
+llvm::Value *MicrosoftCXXABI::performReturnAdjustment(
+ CodeGenFunction &CGF, Address Ret,
+ const CXXRecordDecl * /*UnadjustedClass*/, 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 a6d9f42..f42e28b 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1468,6 +1468,17 @@ void 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);
}
}
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index 1a4bd02..40ac6dc 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -32,6 +32,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;
@@ -205,6 +209,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) \
@@ -271,6 +281,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 367ae53..a73ea0b 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 c528917..a07f7ad 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 99b4003..7e6c7d7 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6281,6 +6281,116 @@ 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 ED = VTablePointerAuthenticationAttr::ExtraDiscrimination::
+ DefaultExtraDiscrimination;
+ if (AL.getNumArgs() > 2) {
+ if (AL.isArgIdent(2)) {
+ IdentifierLoc *IL = AL.getArgAsIdent(2);
+ if (!VTablePointerAuthenticationAttr::ConvertStrToExtraDiscrimination(
+ IL->Ident->getName(), ED)) {
+ S.Diag(IL->Loc, diag::err_invalid_extra_discrimination) << IL->Ident;
+ AL.setInvalid();
+ }
+ if (ED == 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 (ED == VTablePointerAuthenticationAttr::CustomDiscrimination) {
+ if (NumArgs < 4) {
+ S.Diag(AL.getLoc(), diag::err_missing_custom_discrimination) << AL << 4;
+ AL.setInvalid();
+ return;
+ }
+ if (NumArgs > 4) {
+ S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 4;
+ AL.setInvalid();
+ }
+
+ if (!AL.isArgExpr(3) || !S.checkUInt32Argument(AL, AL.getArgAsExpr(3),
+ CustomDiscriminationValue)) {
+ S.Diag(AL.getLoc(), diag::err_invalid_custom_discrimination);
+ AL.setInvalid();
+ }
+ } else if (NumArgs > 3) {
+ S.Diag(AL.getLoc(), diag::err_attribute_too_many_arguments) << AL << 3;
+ AL.setInvalid();
+ }
+
+ Decl->addAttr(::new (S.Context) VTablePointerAuthenticationAttr(
+ S.Context, AL, KeyType, AddressDiversityMode, ED,
+ CustomDiscriminationValue));
+}
+
//===----------------------------------------------------------------------===//
// Top Level Sema Entry Points
//===----------------------------------------------------------------------===//
@@ -7150,6 +7260,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 9b22010..ffa6bca 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7116,6 +7116,10 @@ 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);
@@ -10500,6 +10504,39 @@ 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;
+ Diag(RD.getAttr<VTablePointerAuthenticationAttr>()->getLocation(),
+ diag::err_non_top_level_vtable_pointer_auth)
+ << &RD << Base;
+ PrimaryBase = Base;
+ }
+
+ if (!RD.isPolymorphic())
+ Diag(RD.getAttr<VTablePointerAuthenticationAttr>()->getLocation(),
+ diag::err_non_polymorphic_vtable_pointer_auth)
+ << &RD;
+}
+
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 4a2f3a6..db44cfe 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14217,6 +14217,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 0000000..6c360046
--- /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 1a0e98a..0c14c97 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
@@ -110,7 +110,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: mul i64 %[[#]], -4658895280553007687, !nosanitize
// [...]
// 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 0000000..c2f20d5
--- /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 0000000..996829a
--- /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 0000000..7bcf1fb
--- /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 0000000..e6497b3
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-explicit-vtable-pointer-control.cpp
@@ -0,0 +1,564 @@
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \
+// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,NODISC %s
+
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \
+// RUN: -fptrauth-vtable-pointer-type-discrimination \
+// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,TYPE %s
+
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \
+// RUN: -fptrauth-vtable-pointer-address-discrimination \
+// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,ADDR %s
+
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \
+// RUN: -fptrauth-vtable-pointer-type-discrimination \
+// RUN: -fptrauth-vtable-pointer-address-discrimination \
+// RUN: -emit-llvm -o - | FileCheck --check-prefixes=CHECK,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, 42424) 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_multiple_primary(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 *);
+
+extern "C" {
+
+// CHECK: @TVDisc_NoExplicitAuth = global i32 [[DISC_DEFAULT:49565]], align 4
+int TVDisc_NoExplicitAuth = ptrauth_string_discriminator("_ZTVN5test114NoExplicitAuthE");
+
+// CHECK: @TVDisc_ExplicitlyDisableAuth = global i32 [[DISC_DISABLED:24369]], align 4
+int TVDisc_ExplicitlyDisableAuth = ptrauth_string_discriminator("_ZTVN5test121ExplicitlyDisableAuthE");
+
+// CHECK: @TVDisc_ExplicitAddressDiscrimination = global i32 [[DISC_ADDR:56943]], align 4
+int TVDisc_ExplicitAddressDiscrimination = ptrauth_string_discriminator("_ZTVN5test129ExplicitAddressDiscriminationE");
+
+// CHECK: @TVDisc_ExplicitNoAddressDiscrimination = global i32 [[DISC_NO_ADDR:6022]], align 4
+int TVDisc_ExplicitNoAddressDiscrimination = ptrauth_string_discriminator("_ZTVN5test131ExplicitNoAddressDiscriminationE");
+
+// CHECK: @TVDisc_ExplicitNoExtraDiscrimination = global i32 [[DISC_NO_EXTRA:9072]], align 4
+int TVDisc_ExplicitNoExtraDiscrimination = ptrauth_string_discriminator("_ZTVN5test129ExplicitNoExtraDiscriminationE");
+
+// CHECK: @TVDisc_ExplicitTypeDiscrimination = global i32 [[DISC_TYPE:6177]], align 4
+int TVDisc_ExplicitTypeDiscrimination = ptrauth_string_discriminator("_ZTVN5test126ExplicitTypeDiscriminationE");
+
+
+// CHECK-LABEL: define void @test_default(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_default(NoExplicitAuth *a) {
+ a->f();
+}
+
+// CHECK-LABEL: define void @test_disabled(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+// CHECK-NOT: call i64 @llvm.ptrauth.auth
+void test_disabled(ExplicitlyDisableAuth *a) {
+ a->f();
+}
+
+// CHECK-LABEL: define void @test_addr_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// TYPE: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// TYPE: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]])
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_addr_disc(ExplicitAddressDiscrimination *a) {
+ a->f();
+}
+
+// CHECK-LABEL: define void @test_no_addr_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]])
+//
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]])
+void test_no_addr_disc(ExplicitNoAddressDiscrimination *a) {
+ a->f();
+}
+
+// CHECK-LABEL: define void @test_no_extra_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+void test_no_extra_disc(ExplicitNoExtraDiscrimination *a) {
+ a->f();
+}
+
+// CHECK-LABEL: define void @test_type_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]])
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]])
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_type_disc(ExplicitTypeDiscrimination *a) {
+ a->f();
+}
+
+// CHECK-LABEL: define void @test_custom_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = load ptr, ptr {{%.*}}, align 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_custom_disc(ExplicitCustomDiscrimination *a) {
+ a->f();
+}
+
+//
+// Test some simple single inheritance cases.
+// Codegen should be the same as the simple cases above once we have a vtable.
+//
+
+// CHECK-LABEL: define void @test_subclass_default(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_subclass_default(NoExplicitAuth *a) {
+ make_subclass(a)->f();
+}
+
+// CHECK-LABEL: define void @test_subclass_disabled(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+// CHECK-NOT: call i64 @llvm.ptrauth.auth
+void test_subclass_disabled(ExplicitlyDisableAuth *a) {
+ make_subclass(a)->f();
+}
+
+// CHECK-LABEL: define void @test_subclass_addr_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// TYPE: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// TYPE: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]])
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_ADDR]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_subclass_addr_disc(ExplicitAddressDiscrimination *a) {
+ make_subclass(a)->f();
+}
+
+// CHECK-LABEL: define void @test_subclass_no_addr_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]])
+//
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_NO_ADDR]])
+void test_subclass_no_addr_disc(ExplicitNoAddressDiscrimination *a) {
+ make_subclass(a)->f();
+}
+
+// CHECK-LABEL: define void @test_subclass_no_extra_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+void test_subclass_no_extra_disc(ExplicitNoExtraDiscrimination *a) {
+ make_subclass(a)->f();
+}
+
+// CHECK-LABEL: define void @test_subclass_type_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]])
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_TYPE]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]])
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_TYPE]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_subclass_type_disc(ExplicitTypeDiscrimination *a) {
+ make_subclass(a)->f();
+}
+
+// CHECK-LABEL: define void @test_subclass_custom_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTADDR:%.*]] = call noundef ptr @_ZN5test113make_subclass
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_subclass_custom_disc(ExplicitCustomDiscrimination *a) {
+ make_subclass(a)->f();
+}
+
+
+//
+// Test some simple multiple inheritance cases.
+// Codegen should be the same as the simple cases above once we have a vtable.
+//
+
+// CHECK-LABEL: define void @test_multiple_default(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[CALL:%.*]] = call noundef ptr @_ZN5test121make_multiple_primary
+// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_multiple_default(NoExplicitAuth *a) {
+ make_multiple_primary(a)->f();
+}
+
+// CHECK-LABEL: define void @test_multiple_disabled(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[CALL:%.*]] = call noundef ptr @_ZN5test121make_multiple_primary
+// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+// CHECK-NOT: call i64 @llvm.ptrauth.auth
+void test_multiple_disabled(ExplicitlyDisableAuth *a) {
+ make_multiple_primary(a)->f();
+}
+
+// CHECK-LABEL: define void @test_multiple_custom_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[CALL:%.*]] = call noundef ptr @_ZN5test121make_multiple_primary
+// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_multiple_custom_disc(ExplicitCustomDiscrimination *a) {
+ make_multiple_primary(a)->f();
+}
+
+//
+// Test some virtual inheritance cases.
+// Codegen should be the same as the simple cases above once we have a vtable,
+// but twice for vtt/vtable. The names in the vtt version have "VTT" prefixes.
+//
+
+// CHECK-LABEL: define void @test_virtual_default(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTTADDR:%.*]] = call noundef ptr @_ZN5test120make_virtual_primary
+// CHECK: [[VTTABLE:%.*]] = load ptr, ptr [[VTTADDR]], align 8
+//
+// NODISC: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[DISC_DEFAULT]])
+//
+// ADDR: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64
+// ADDR: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[VTTADDRI64]])
+//
+// BOTH: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTTADDRI64]], i64 [[DISC_DEFAULT]])
+// BOTH: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[BLEND]])
+
+// CHECK: [[AUTHEDPTR:%.*]] = inttoptr i64 [[AUTHED]] to ptr
+// CHECK: [[VBOFFPTR:%.*]] = getelementptr i8, ptr [[AUTHEDPTR]], i64 -48
+// CHECK: [[VBOFFSET:%.*]] = load i64, ptr [[VBOFFPTR]]
+// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[VTTADDR]], i64 [[VBOFFSET]]
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 0)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[DISC_DEFAULT]])
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[VTADDRI64]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 [[DISC_DEFAULT]])
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_virtual_default(NoExplicitAuth *a) {
+ make_virtual_primary(a)->f();
+}
+
+// CHECK-LABEL: define void @test_virtual_disabled(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK-NOT: call i64 @llvm.ptrauth.auth
+void test_virtual_disabled(ExplicitlyDisableAuth *a) {
+ make_virtual_primary(a)->f();
+}
+
+// CHECK-LABEL: define void @test_virtual_custom_disc(ptr noundef {{%.*}}) {{#.*}} {
+// CHECK: [[VTTADDR:%.*]] = call noundef ptr @_ZN5test120make_virtual_primary
+// CHECK: [[VTTABLE:%.*]] = load ptr, ptr [[VTTADDR]], align 8
+//
+// NODISC: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 42424)
+//
+// TYPE: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 42424)
+//
+// ADDR: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64
+// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTTADDRI64]], i64 42424)
+// ADDR: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// BOTH: [[VTTADDRI64:%.*]] = ptrtoint ptr [[VTTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTTADDRI64]], i64 42424)
+// BOTH: [[VTTABLEI64:%.*]] = ptrtoint ptr [[VTTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTTABLEI64]], i32 2, i64 [[BLEND]])
+
+// CHECK: [[AUTHEDPTR:%.*]] = inttoptr i64 [[AUTHED]] to ptr
+// CHECK: [[VBOFFPTR:%.*]] = getelementptr i8, ptr [[AUTHEDPTR]], i64 -48
+// CHECK: [[VBOFFSET:%.*]] = load i64, ptr [[VBOFFPTR]]
+// CHECK: [[VTADDR:%.*]] = getelementptr inbounds i8, ptr [[VTTADDR]], i64 [[VBOFFSET]]
+// CHECK: [[VTABLE:%.*]] = load ptr, ptr [[VTADDR]], align 8
+//
+// NODISC: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// NODISC: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// TYPE: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// TYPE: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 42424)
+//
+// ADDR: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// ADDR: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// ADDR: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// ADDR: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+//
+// BOTH: [[VTADDRI64:%.*]] = ptrtoint ptr [[VTADDR]] to i64
+// BOTH: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VTADDRI64]], i64 42424)
+// BOTH: [[VTABLEI64:%.*]] = ptrtoint ptr [[VTABLE]] to i64
+// BOTH: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLEI64]], i32 2, i64 [[BLEND]])
+void test_virtual_custom_disc(ExplicitCustomDiscrimination *a) {
+ make_virtual_primary(a)->f();
+}
+
+} // extern "C"
+} // namespace test1
diff --git a/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp
new file mode 100644
index 0000000..b4a8784
--- /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 0000000..a85c8c4
--- /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 0000000..563d607
--- /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 0000000..00b1cbd
--- /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 c703162..72c59cb 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: {{%.*}} = mul 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: {{%.*}} = mul 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: {{%.*}} = mul 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 9973269..28df04c 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 0000000..41bbba0
--- /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 0000000..3a33861
--- /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@-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@-1{{invalid authentication key 'wat'}}
+TEST_AUTH(InvalidAddressDiscrimination, no_authentication, wat, default_extra_discrimination);
+// expected-error@-1{{invalid address discrimination mode 'wat'}}
+TEST_AUTH(InvalidExtraDiscrimination, no_authentication, default_address_discrimination, wat);
+// expected-error@-1{{invalid extra discrimination selection 'wat'}}
+TEST_AUTH(InvalidNoCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination);
+// expected-error@-1{{missing custom discrimination}}
+TEST_AUTH(InvalidCustomDiscrimination, no_authentication, default_address_discrimination, custom_discrimination, wat);
+// expected-error@-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@-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@-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@-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@-1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidPolymorphic>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+// expected-error@-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@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidPolymorphic>' requested here}}
+ExplicitlyAuthedMonomorphicTemplateClass<ValidMonomorphic> test2;
+// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClass<basic::ValidMonomorphic>' requested here}}
+ExplicitlyAuthedPolymorphicTemplateClass<ValidPolymorphic> test3;
+// expected-note@-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@-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@-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@-1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase'}}
+ // expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedMonomorphicTemplateClassNoBase<int>'}}
+};
+
+ExplicitlyAuthedMonomorphicTemplateClassNoBase<int> X;
+// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedMonomorphicTemplateClassNoBase<int>' requested here}}
+
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedTemplateClassValidBase : ValidMonomorphic {
+ // expected-error@-1{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase'}}
+ // expected-error@-2{{cannot set vtable pointer authentication on monomorphic type 'ExplicitlyAuthedTemplateClassValidBase<int>'}}
+};
+
+ExplicitlyAuthedTemplateClassValidBase<int> Y;
+// expected-note@-1{{in instantiation of template class 'basic::ExplicitlyAuthedTemplateClassValidBase<int>' requested here}}
+
+template <typename T>
+struct generic_authenticated ExplicitlyAuthedTemplateClassInvalidBase : ValidPolymorphic {
+ // expected-error@-1{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+ // expected-error@-2{{cannot set vtable pointer authentication on 'ExplicitlyAuthedTemplateClassInvalidBase<int>' which is a subclass of polymorphic type 'ValidPolymorphic'}}
+};
+
+ExplicitlyAuthedTemplateClassInvalidBase<int> Z;
+// expected-note@-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@-1 {{unknown type name 'MissingDecl'}}
+public:
+ virtual ~TestPolymorphicTemplateSpecialization();
+};
+template <class test1>
+class generic_authenticated TestPolymorphicTemplateSpecialization<test1, double>
+// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization<test1, double>'}}
+// expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestPolymorphicTemplateSpecialization<double, double>'}}
+{
+};
+
+TestPolymorphicTemplateSpecialization<double, float> b;
+TestPolymorphicTemplateSpecialization<double, double> b2;
+// expected-note@-1 {{in instantiation of template class 'basic::TestPolymorphicTemplateSpecialization<double, double>' requested here}}
+
+template <typename A> class generic_authenticated TestMonomorphic {};
+// expected-error@-1 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphic'}}
+// expected-error@-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@-1 {{cannot set vtable pointer authentication on 'TestMonomorphicSubclass2<int>' which is a subclass of polymorphic type 'TestMonomorphic<int>'}}
+ // expected-error@-2 {{cannot set vtable pointer authentication on monomorphic type 'TestMonomorphicSubclass2<double>'}}
+ // expected-note@-3 {{in instantiation of template class 'basic::TestMonomorphic<double>' requested here}}
+};
+
+TestMonomorphicSubclass tms_1;
+TestMonomorphicSubclass2<int> tms2_1;
+// expected-note@-1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<int>' requested here}}
+TestMonomorphicSubclass2<double> tms2_2;
+// expected-note@-1 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<double>' requested here}}
+// expected-note@-2 {{in instantiation of template class 'basic::TestMonomorphicSubclass2<double>' requested here}}
+
+template <typename T>
+class generic_authenticated dependent_type {
+ // expected-error@-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@-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@-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@-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 307334a..7bf6091 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -2578,6 +2578,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>(
@@ -4959,6 +4985,7 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS) {
emitClangAttrTypeArgList(Records, OS);
emitClangAttrLateParsedList(Records, OS);
emitClangAttrLateParsedExperimentalList(Records, OS);
+ emitClangAttrStrictIdentifierArgAtIndexList(Records, OS);
}
void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,