[clang] [PAC] Add support for __ptrauth type qualifier (PR #100830)
Akira Hatanaka via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 1 11:36:11 PDT 2024
https://github.com/ahatanak updated https://github.com/llvm/llvm-project/pull/100830
>From 51c21ae0348b2ddb1b55f3ef9f74c2c36f4d21ae Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Fri, 26 Jul 2024 09:32:07 -0700
Subject: [PATCH 1/5] [PAC] Add support for __ptrauth type qualifier
The qualifier allows programmer to directly control how pointers are
signed when they are stored in a particular variable.
The qualifier takes three arguments: the signing key, a flag specifying
whether address discrimination should be used, and a non-negative
integer that is used for additional discrimination.
```
typedef void (*my_callback)(const void*);
my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback;
```
Co-Authored-By: John McCall rjmccall at apple.com
---
clang/include/clang/AST/Type.h | 21 +-
clang/include/clang/Basic/Attr.td | 8 +
clang/include/clang/Basic/AttrDocs.td | 146 ++++
.../clang/Basic/DiagnosticSemaKinds.td | 46 +-
clang/include/clang/Basic/Features.def | 1 +
clang/include/clang/Basic/TokenKinds.def | 1 +
clang/include/clang/Parse/Parser.h | 2 +
clang/include/clang/Sema/Sema.h | 11 +
clang/lib/AST/ASTContext.cpp | 1 +
clang/lib/AST/DeclCXX.cpp | 27 +
clang/lib/AST/ItaniumMangle.cpp | 20 +
clang/lib/AST/TypePrinter.cpp | 40 +
clang/lib/CodeGen/CGClass.cpp | 3 +
clang/lib/CodeGen/CGDebugInfo.cpp | 19 +-
clang/lib/CodeGen/CGDecl.cpp | 8 +-
clang/lib/CodeGen/CGExpr.cpp | 48 ++
clang/lib/CodeGen/CGExprConstant.cpp | 27 +-
clang/lib/CodeGen/CGExprScalar.cpp | 67 ++
clang/lib/CodeGen/CGPointerAuth.cpp | 140 ++++
clang/lib/CodeGen/CodeGenFunction.h | 20 +
clang/lib/Parse/ParseDecl.cpp | 47 ++
clang/lib/Sema/SemaCast.cpp | 16 +-
clang/lib/Sema/SemaChecking.cpp | 63 ++
clang/lib/Sema/SemaDecl.cpp | 14 +-
clang/lib/Sema/SemaDeclCXX.cpp | 37 +
clang/lib/Sema/SemaExpr.cpp | 14 +
clang/lib/Sema/SemaExprCXX.cpp | 5 +
clang/lib/Sema/SemaObjCProperty.cpp | 4 +
clang/lib/Sema/SemaOverload.cpp | 11 +
clang/lib/Sema/SemaType.cpp | 87 ++
clang/lib/Sema/TreeTransform.h | 11 +
clang/test/CodeGen/ptrauth-debuginfo.c | 32 +
.../test/CodeGen/ptrauth-qualifier-function.c | 75 ++
.../CodeGen/ptrauth-qualifier-loadstore.c | 744 ++++++++++++++++++
clang/test/CodeGen/ptrauth-qualifier.c | 87 ++
.../CodeGenCXX/ptrauth-qualifier-struct.cpp | 167 ++++
.../CodeGenObjCXX/ptrauth-struct-cxx-abi.mm | 35 +
clang/test/Preprocessor/ptrauth_feature.c | 10 +
clang/test/Sema/atomic-ops-ptrauth.c | 117 +++
clang/test/Sema/ptrauth-qualifier.c | 84 ++
clang/test/SemaCXX/ptrauth-qualifier.cpp | 141 ++++
.../SemaCXX/ptrauth-template-parameters.cpp | 20 +
clang/test/SemaObjC/ptrauth-qualifier.m | 55 ++
43 files changed, 2518 insertions(+), 14 deletions(-)
create mode 100644 clang/test/CodeGen/ptrauth-debuginfo.c
create mode 100644 clang/test/CodeGen/ptrauth-qualifier-function.c
create mode 100644 clang/test/CodeGen/ptrauth-qualifier-loadstore.c
create mode 100644 clang/test/CodeGen/ptrauth-qualifier.c
create mode 100644 clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp
create mode 100644 clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm
create mode 100644 clang/test/Sema/atomic-ops-ptrauth.c
create mode 100644 clang/test/Sema/ptrauth-qualifier.c
create mode 100644 clang/test/SemaCXX/ptrauth-qualifier.cpp
create mode 100644 clang/test/SemaCXX/ptrauth-template-parameters.cpp
create mode 100644 clang/test/SemaObjC/ptrauth-qualifier.m
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 72723c7c56e07..b84d808410583 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -307,6 +307,12 @@ class PointerAuthQualifier {
return Result;
}
+ std::string getAsString() const;
+ std::string getAsString(const PrintingPolicy &Policy) const;
+
+ bool isEmptyWhenPrinted(const PrintingPolicy &Policy) const;
+ void print(raw_ostream &OS, const PrintingPolicy &Policy) const;
+
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Data); }
};
@@ -556,7 +562,7 @@ class Qualifiers {
bool hasAddressSpace() const { return Mask & AddressSpaceMask; }
LangAS getAddressSpace() const {
- return static_cast<LangAS>(Mask >> AddressSpaceShift);
+ return static_cast<LangAS>((Mask & AddressSpaceMask) >> AddressSpaceShift);
}
bool hasTargetSpecificAddressSpace() const {
return isTargetAddressSpace(getAddressSpace());
@@ -815,6 +821,9 @@ class Qualifiers {
static_assert(sizeof(PointerAuthQualifier) == sizeof(uint32_t),
"PointerAuthQualifier must be 32 bits");
+ static constexpr uint64_t PtrAuthShift = 32;
+ static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift;
+
static constexpr uint64_t UMask = 0x8;
static constexpr uint64_t UShift = 3;
static constexpr uint64_t GCAttrMask = 0x30;
@@ -822,10 +831,8 @@ class Qualifiers {
static constexpr uint64_t LifetimeMask = 0x1C0;
static constexpr uint64_t LifetimeShift = 6;
static constexpr uint64_t AddressSpaceMask =
- ~(CVRMask | UMask | GCAttrMask | LifetimeMask);
+ ~(CVRMask | UMask | GCAttrMask | LifetimeMask | PtrAuthMask);
static constexpr uint64_t AddressSpaceShift = 9;
- static constexpr uint64_t PtrAuthShift = 32;
- static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift;
};
class QualifiersAndAtomic {
@@ -1460,6 +1467,12 @@ class QualType {
return getQualifiers().getPointerAuth();
}
+ bool hasAddressDiscriminatedPointerAuth() const {
+ if (auto ptrauth = getPointerAuth())
+ return ptrauth.isAddressDiscriminated();
+ return false;
+ }
+
enum PrimitiveDefaultInitializeKind {
/// The type does not fall into any of the following categories. Note that
/// this case is zero-valued so that values of this enum can be used as a
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 4825979a974d2..f9b447e85707d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3339,6 +3339,14 @@ def ObjCRequiresPropertyDefs : InheritableAttr {
let SimpleHandler = 1;
}
+def PointerAuth : TypeAttr {
+ let Spellings = [CustomKeyword<"__ptrauth">];
+ let Args = [IntArgument<"Key">,
+ BoolArgument<"AddressDiscriminated", 1>,
+ IntArgument<"ExtraDiscriminator", 1>];
+ let Documentation = [PtrAuthDocs];
+}
+
def Unused : InheritableAttr {
let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">,
C23<"", "maybe_unused", 202106>];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 99738812c8157..547cb010ebc84 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1758,6 +1758,152 @@ Also see the documentation for `@available
}];
}
+def PtrAuthDocs : Documentation {
+ let Category = DocCatVariable;
+ let Heading = "__ptrauth, __ptrauth_restricted_intptr";
+ let Content = [{
+The ``__ptrauth`` qualifier allows the programmer to directly control
+how pointers are signed when they are stored in a particular variable.
+This can be used to strengthen the default protections of pointer
+authentication and make it more difficult for an attacker to escalate
+an ability to alter memory into full control of a process.
+
+.. code-block:: c
+
+ #include <ptrauth.h>
+
+ typedef void (*my_callback)(const void*);
+ my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback;
+
+The first argument to ``__ptrauth`` is the name of the signing key.
+Valid key names for the target are defined in ``<ptrauth.h>``.
+
+On ARM64, there are four keys:
+
+- ``ptrauth_key_process_independent_data``
+- ``ptrauth_key_process_dependent_data``
+- ``ptrauth_key_process_independent_code``
+- ``ptrauth_key_process_dependent_code``
+
+In general, prefer using a code key for function pointers and a data key
+for object pointers. The ARM64 architecture allows loads and calls to
+execute more efficiently when the pointer is signed with an appropriate
+key. Using code keys only for function pointers also substantially lessens
+the risk of creating a so-called "signing oracle" for function pointers;
+see the general pointer authentication language documentation.
+
+Using a process-dependent key provides stronger protection against
+cross-process attacks. However, it also inhibits certain memory
+optimizations when a shared library is loaded into multiple processes.
+Using a process-independent key also allows signed pointers to be passed
+in shared memory. Note that even the process-independent keys may change
+after a reboot, so signed values should never be serialized.
+
+The second argument to ``__ptrauth`` is a flag (0 or 1) specifying whether
+the object should use address discrimination. If only one argument is
+given, the flag defaults to 0. Address discrimination provides strong
+protection against attacks which copy signed pointers around in memory.
+An attacker cannot usefully copy an arbitrary signed pointer over an
+address-discriminated object. Nor can a value taken from an
+address-discriminated object be usefully copied over some other signed
+pointer. However, it is more expensive to copy values from one
+address-discriminated object to another, even if the other arguments to
+``__ptrauth`` are the same, and it is not valid to copy them with
+``memcpy``. It is also not valid to map memory containing an
+address-discriminated object into different places in the address
+space, e.g. with ``mmap``.
+
+The third argument to ``__ptrauth`` is a small non-negative integer
+which allows additional discrimination between objects. Using a
+unique extra discriminator provides strong protection against attacks
+which work by substituting one signed value for another. For example,
+an attacker cannot usefully overwrite an object with a pointer from an
+object using a different extra discriminator; this protection is similar
+to the protection offered by address discrimination. A unique extra
+discriminator also protects against "slide" attacks where an attacker
+alters a pointer instead of altering the memory that the pointer points to.
+The extra discriminator must be a constant expression. On ARM64,
+its value must be between 0 and 65535. If the argument is not provided,
+the default value is 0. It is generally preferable not to use the value 0,
+especially with the process-independent keys, as this combination is used
+in various places in the standard language ABI.
+
+The type qualified by ``__ptrauth`` must be a pointer type. Currently
+only C pointer types are allowed and not block pointers, Objective-C
+object pointers, or C++ references. ``__ptrauth`` is parsed and interpreted
+using the same language rules as qualifiers like ``const`` and ``volatile``.
+For example:
+
+.. code-block:: c
+
+ __ptrauth(...) int *ex0; /* invalid: qualifies 'int', which is not a pointer type */
+ int * __ptrauth(...) ex1; /* valid: ex1 has qualified type */
+ int * __ptrauth(...) *ex2; /* valid: ex2 is a pointer to a qualified object */
+
+ typedef int *intp;
+ __ptrauth(...) intp ex3; /* valid: ex3 has qualified type */
+ intp __ptrauth(...) ex4; /* valid: means the exact same thing as ex3 */
+
+If a ``__ptrauth``-qualified l-value of function pointer type is
+used as the function operand of a call expression, the function pointer
+will be authenticated "atomically" with the call, such that an attacker
+will not be able to corrupt the destination of the call even in the
+presence of undefined behavior. (That is, the compiler must not
+leave an un-signed pointer that it will later unconditionally trust
+in a place where it could be feasibly overwritten by an attacker,
+such as the stack or a callee-save register during an intervening call.
+The compiler is not required to protect against improbable attacks
+such as corruption of the register file, as might occur with a
+corrupted kernel. It also need not guard against jumps to an arbitrary
+place in the instruction stream, since such jumps would require an
+attacker to already fully control the PC.)
+
+If the ABI specifies that a pointer is always signed --- that is,
+if the pointer is a function pointer and the target uses ABI function
+pointer authentication --- then signing and authenticating it as part
+of a load/store actually means resigning it to/from the standard ABI
+signature schema. Similarly, if both operands of a simple assignment
+operator are ``__ptrauth``-qualified, the pointer copied by the
+assignment is resigned from the right-hand operand's schema to the
+left-hand operand's schema. These resigning operations are also done
+"atomically" in the same sense as above.
+
+As a final guarantee, if the right-hand operand of an assignment or
+the expression used to initialize a ``__ptrauth``-qualified object is
+a direct reference to an object or function (e.g. ``&my_var``), the
+signing of that pointer is atomic with the evaluaton of the reference
+in this same sense.
+
+Otherwise, there are no guarantees of atomicity, and it is the
+programmer's responsibility to avoid allowing a store into a
+``__ptrauth``-qualified object to create a potential "signing oracle"
+which an attacker could use to sign an arbitrary pointer of their choice.
+Such oracles are particularly problematic when the signing uses a code
+key because the oracle could potentially be used to allow an attacker
+to construct a validly-signed function pointer, v-table entry, or
+return address that points to an arbitrary instruction, allowing them
+to completely take over the PC. Programmers attempting to use
+``__ptrauth`` to protect a data pointer, or to protect function pointers
+on targets that do not use ABI function pointer authentication, should
+aim to maintain a "chain of authentication" from initialization all
+the way to the point at which the pointer is used. If this is infeasible,
+they should consider using ``ptrauth_sign_generic_data`` instead.
+
+Types that are written in r-value positions, such as return types,
+parameter types, and cast types, may not be ``__ptrauth``-qualified
+at the outermost level. This may be supported in the future.
+
+In C++, the arguments to ``__ptrauth`` may not be instantiation-dependent.
+This may be supported in the future.
+
+This feature may be tested for with ``__has_feature(ptrauth_qualifier)``.
+It is enabled whenever the ``ptrauth`` intrinsics are enabled.
+
+``<ptrauth.h>`` provides predefined qualifiers for various language
+features that implicitly use pointer authentication.
+ }];
+}
+
def ExternalSourceSymbolDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 810abe4f23e31..db07874ffcbbf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -956,6 +956,25 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
"%select{subtraction|addition}0 of address-of-label expressions is not "
"supported with ptrauth indirect gotos">;
+// __ptrauth qualifier
+def err_ptrauth_qualifier_invalid : Error<
+ "%select{return types|parameter types|properties}2 may not be qualified with %select{__ptrauth|__ptrauth_restricted_intptr}1; type is %0">;
+def err_ptrauth_qualifier_cast : Error<
+ "cast types may not be qualified with __ptrauth; type is %0">;
+def err_ptrauth_qualifier_nonpointer : Error<
+ "__ptrauth qualifier may only be applied to pointer types; type here is %0">;
+def err_ptrauth_qualifier_redundant : Error<
+ "type %0 is already %1-qualified">;
+def err_ptrauth_qualifier_bad_arg_count : Error<
+ "%0 qualifier must take between 1 and 3 arguments">;
+def err_ptrauth_arg_not_ice : Error<
+ "argument to __ptrauth must be an integer constant expression">;
+def err_ptrauth_address_discrimination_invalid : Error<
+ "address discrimination flag for __ptrauth must be 0 or 1; value is %0">;
+def err_ptrauth_extra_discriminator_invalid : Error<
+ "extra discriminator for __ptrauth must be between "
+ "0 and %1; value is %0">;
+
/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -3870,7 +3889,8 @@ def note_cannot_use_trivial_abi_reason : Note<
"its copy constructors and move constructors are all deleted|"
"it is polymorphic|"
"it has a base of a non-trivial class type|it has a virtual base|"
- "it has a __weak field|it has a field of a non-trivial class type}1">;
+ "it has a __weak field|it has a field of a non-trivial class type|"
+ "it has an address-discriminated __ptrauth field}1">;
// Availability attribute
def warn_availability_unknown_platform : Warning<
@@ -4927,6 +4947,10 @@ def note_ovl_candidate_bad_ownership : Note<
"%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}4 ownership,"
" but parameter has %select{no|__unsafe_unretained|__strong|__weak|"
"__autoreleasing}5 ownership">;
+def note_ovl_candidate_bad_ptrauth : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "%ordinal8 argument (%3) has %select{no ptrauth|%5}4 qualifier,"
+ " but parameter has %select{no ptrauth|%7}6 qualifier">;
def note_ovl_candidate_bad_cvr_this : Note<
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
"'this' argument has type %3, but method is not marked "
@@ -6005,7 +6029,7 @@ def note_deleted_special_member_class_subobject : Note<
"%select{default|corresponding|default|default|default}4 constructor}0|"
"destructor}5"
"%select{||s||}4"
- "|is an ObjC pointer}6">;
+ "|is an ObjC pointer|has an address-discriminated ptrauth qualifier}6">;
def note_deleted_default_ctor_uninit_field : Note<
"%select{default constructor of|constructor inherited by}0 "
"%1 is implicitly deleted because field %2 of "
@@ -8811,6 +8835,19 @@ def err_typecheck_incompatible_ownership : Error<
"sending to parameter of different type}0,1"
"|%diff{casting $ to type $|casting between types}0,1}2"
" changes retain/release properties of pointer">;
+def err_typecheck_incompatible_ptrauth : Error<
+ "%select{%diff{assigning $ to $|assigning to different types}1,0"
+ "|%diff{passing $ to parameter of type $|"
+ "passing to parameter of different type}0,1"
+ "|%diff{returning $ from a function with result type $|"
+ "returning from function with different return type}0,1"
+ "|%diff{converting $ to type $|converting between types}0,1"
+ "|%diff{initializing $ with an expression of type $|"
+ "initializing with expression of different type}0,1"
+ "|%diff{sending $ to parameter of type $|"
+ "sending to parameter of different type}0,1"
+ "|%diff{casting $ to type $|casting between types}0,1}2"
+ " changes pointer-authentication of pointee type">;
def err_typecheck_comparison_of_distinct_blocks : Error<
"comparison of distinct block types%diff{ ($ and $)|}0,1">;
@@ -8939,6 +8976,9 @@ def err_atomic_op_needs_non_const_atomic : Error<
def err_atomic_op_needs_non_const_pointer : Error<
"address argument to atomic operation must be a pointer to non-const "
"type (%0 invalid)">;
+def err_atomic_op_needs_non_address_discriminated_pointer : Error<
+ "address argument to %select{atomic|__sync}0 operation must be a pointer to a non address discriminated "
+ "type (%1 invalid)">;
def err_atomic_op_needs_trivial_copy : Error<
"address argument to atomic operation must be a pointer to a "
"trivially-copyable type (%0 invalid)">;
@@ -9205,6 +9245,8 @@ def ext_typecheck_cond_pointer_integer_mismatch : ExtWarn<
"pointer/integer type mismatch in conditional expression"
"%diff{ ($ and $)|}0,1">,
InGroup<DiagGroup<"conditional-type-mismatch">>;
+def err_typecheck_cond_incompatible_ptrauth : Error<
+ "__ptrauth qualification mismatch%diff{ ($ and $)|}0,1">;
def err_typecheck_choose_expr_requires_constant : Error<
"'__builtin_choose_expr' requires a constant expression">;
def warn_unused_expr : Warning<"expression result unused">,
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index dc71ef8f98692..6853a7969e929 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -104,6 +104,7 @@ FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics)
+FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics)
FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls)
FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns)
FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination)
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 8c54661e65cf4..68a214452e28b 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -346,6 +346,7 @@ KEYWORD(_Thread_local , KEYALL)
KEYWORD(__func__ , KEYALL)
KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)
+KEYWORD(__ptrauth , KEYALL)
// C++ 2.11p1: Keywords.
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 35bb1a19d40f0..e3049914289bd 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3151,6 +3151,8 @@ class Parser : public CodeCompletionHandler {
SourceLocation *endLoc = nullptr);
ExprResult ParseExtIntegerArgument();
+ void ParsePtrauthQualifier(ParsedAttributes &Attrs);
+
VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const;
VirtSpecifiers::Specifier isCXX11VirtSpecifier() const {
return isCXX11VirtSpecifier(Tok);
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7bfdaaae45a93..211d3941921ff 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3460,6 +3460,17 @@ class Sema final : public SemaBase {
bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key);
+ enum PointerAuthDiscArgKind {
+ // Address discrimination argument of __ptrauth.
+ PADAK_AddrDiscPtrAuth,
+
+ // Extra discriminator argument of __ptrauth.
+ PADAK_ExtraDiscPtrAuth,
+ };
+
+ bool checkPointerAuthDiscriminatorArg(Expr *arg, PointerAuthDiscArgKind kind,
+ unsigned &intVal);
+
/// Diagnose function specifiers on a declaration of an identifier that
/// does not identify a function.
void DiagnoseFunctionSpecifiers(const DeclSpec &DS);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a465cdfcf3c89..c1dc730eaa2d5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -11046,6 +11046,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() ||
LQuals.getAddressSpace() != RQuals.getAddressSpace() ||
LQuals.getObjCLifetime() != RQuals.getObjCLifetime() ||
+ !LQuals.getPointerAuth().isEquivalent(RQuals.getPointerAuth()) ||
LQuals.hasUnaligned() != RQuals.hasUnaligned())
return {};
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index b573c2713a3aa..c2c550076ea07 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1072,6 +1072,33 @@ void CXXRecordDecl::addedMember(Decl *D) {
} else if (!T.isCXX98PODType(Context))
data().PlainOldData = false;
+ // If a class has an address-discriminated signed pointer member, it is a
+ // non-POD type and its copy constructor, move constructor, copy assignment
+ // operator, move assignment operator are non-trivial.
+ if (PointerAuthQualifier Q = T.getPointerAuth()) {
+ if (Q.isAddressDiscriminated()) {
+ struct DefinitionData &Data = data();
+ Data.PlainOldData = false;
+ Data.HasTrivialSpecialMembers &=
+ ~(SMF_CopyConstructor | SMF_MoveConstructor | SMF_CopyAssignment |
+ SMF_MoveAssignment);
+ setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs);
+
+ // Copy/move constructors/assignment operators of a union are deleted by
+ // default if it has an address-discriminated ptrauth field.
+ if (isUnion()) {
+ data().DefaultedCopyConstructorIsDeleted = true;
+ data().DefaultedMoveConstructorIsDeleted = true;
+ data().DefaultedCopyAssignmentIsDeleted = true;
+ data().DefaultedMoveAssignmentIsDeleted = true;
+ data().NeedOverloadResolutionForCopyConstructor = true;
+ data().NeedOverloadResolutionForMoveConstructor = true;
+ data().NeedOverloadResolutionForCopyAssignment = true;
+ data().NeedOverloadResolutionForMoveAssignment = true;
+ }
+ }
+ }
+
if (T->isReferenceType()) {
if (!Field->hasInClassInitializer())
data().HasUninitializedReferenceMember = true;
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index d46d621d4c7d4..1b78eeb29f9da 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2822,6 +2822,26 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals, const DependentAddressSp
if (Quals.hasUnaligned())
mangleVendorQualifier("__unaligned");
+ // __ptrauth. Note that this is parameterized.
+ if (auto ptrauth = Quals.getPointerAuth()) {
+ mangleVendorQualifier("__ptrauth");
+ // For now, since we only allow non-dependent arguments, we can just
+ // inline the mangling of those arguments as literals. We treat the
+ // key and extra-discriminator arguments as 'unsigned int' and the
+ // address-discriminated argument as 'bool'.
+ Out << "I"
+ "Lj"
+ << ptrauth.getKey()
+ << "E"
+ "Lb"
+ << unsigned(ptrauth.isAddressDiscriminated())
+ << "E"
+ "Lj"
+ << ptrauth.getExtraDiscriminator()
+ << "E"
+ "E";
+ }
+
// Remaining ARC ownership qualifiers.
switch (Quals.getObjCLifetime()) {
case Qualifiers::OCL_None:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index ffec3ef9d2269..e5fd7b47cb9c2 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1950,6 +1950,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::Ptr64:
case attr::SPtr:
case attr::UPtr:
+ case attr::PointerAuth:
case attr::AddressSpace:
case attr::CmseNSCall:
case attr::AnnotateType:
@@ -2413,6 +2414,33 @@ void clang::printTemplateArgumentList(raw_ostream &OS,
printTo(OS, Args, Policy, TPL, /*isPack*/ false, /*parmIndex*/ 0);
}
+std::string PointerAuthQualifier::getAsString() const {
+ LangOptions LO;
+ return getAsString(PrintingPolicy(LO));
+}
+
+std::string PointerAuthQualifier::getAsString(const PrintingPolicy &P) const {
+ SmallString<64> Buf;
+ llvm::raw_svector_ostream StrOS(Buf);
+ print(StrOS, P);
+ return StrOS.str().str();
+}
+
+bool PointerAuthQualifier::isEmptyWhenPrinted(const PrintingPolicy &P) const {
+ return !isPresent();
+}
+
+void PointerAuthQualifier::print(raw_ostream &OS,
+ const PrintingPolicy &P) const {
+ if (!isPresent())
+ return;
+
+ OS << "__ptrauth(";
+ OS << getKey();
+ OS << "," << unsigned(isAddressDiscriminated()) << ","
+ << getExtraDiscriminator() << ")";
+}
+
std::string Qualifiers::getAsString() const {
LangOptions LO;
return getAsString(PrintingPolicy(LO));
@@ -2442,6 +2470,10 @@ bool Qualifiers::isEmptyWhenPrinted(const PrintingPolicy &Policy) const {
if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime))
return false;
+ if (auto pointerAuth = getPointerAuth())
+ if (!pointerAuth.isEmptyWhenPrinted(Policy))
+ return false;
+
return true;
}
@@ -2548,6 +2580,14 @@ void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy,
}
}
+ if (auto pointerAuth = getPointerAuth()) {
+ if (addSpace)
+ OS << ' ';
+ addSpace = true;
+
+ pointerAuth.print(OS, Policy);
+ }
+
if (appendSpaceIfNonEmpty && addSpace)
OS << ' ';
}
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 667e260f2228d..274fb41484934 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -930,6 +930,9 @@ namespace {
Qualifiers Qual = F->getType().getQualifiers();
if (Qual.hasVolatile() || Qual.hasObjCLifetime())
return false;
+ if (PointerAuthQualifier Q = F->getType().getPointerAuth())
+ if (Q.isAddressDiscriminated())
+ return false;
return true;
}
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 3d8a715b692de..ae4026a84601e 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1015,8 +1015,23 @@ llvm::DIType *CGDebugInfo::CreateQualifiedType(QualType Ty,
// additional ones.
llvm::dwarf::Tag Tag = getNextQualifier(Qc);
if (!Tag) {
- assert(Qc.empty() && "Unknown type qualifier for debug info");
- return getOrCreateType(QualType(T, 0), Unit);
+ if (Qc.getPointerAuth()) {
+ unsigned Key = Qc.getPointerAuth().getKey();
+ bool IsDiscr = Qc.getPointerAuth().isAddressDiscriminated();
+ unsigned ExtraDiscr = Qc.getPointerAuth().getExtraDiscriminator();
+ bool IsaPointer = Qc.getPointerAuth().isIsaPointer();
+ bool AuthenticatesNullValues =
+ Qc.getPointerAuth().authenticatesNullValues();
+ Qc.removePointerAuth();
+ assert(Qc.empty() && "Unknown type qualifier for debug info");
+ auto *FromTy = getOrCreateType(QualType(T, 0), Unit);
+ return DBuilder.createPtrAuthQualifiedType(
+ FromTy, Key, IsDiscr, ExtraDiscr, /*IsaPointer=*/IsaPointer,
+ /*AuthenticatesNullValues=*/AuthenticatesNullValues);
+ } else {
+ assert(Qc.empty() && "Unknown type qualifier for debug info");
+ return getOrCreateType(QualType(T, 0), Unit);
+ }
}
auto *FromTy = getOrCreateType(Qc.apply(CGM.getContext(), T), Unit);
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index c3251bb5ab565..39330853ce705 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -795,7 +795,13 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
LValue lvalue, bool capturedByInit) {
Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime();
if (!lifetime) {
- llvm::Value *value = EmitScalarExpr(init);
+ llvm::Value *value;
+ if (auto ptrauth = lvalue.getQuals().getPointerAuth()) {
+ value = EmitPointerAuthQualify(ptrauth, init, lvalue.getAddress());
+ lvalue.getQuals().removePointerAuth();
+ } else {
+ value = EmitScalarExpr(init);
+ }
if (capturedByInit)
drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D));
EmitNullabilityCheck(lvalue, value, init->getExprLoc());
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 5f58a64d8386c..43dab7b41b8a1 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2190,6 +2190,15 @@ RValue CodeGenFunction::EmitLoadOfAnyValue(LValue LV, AggValueSlot Slot,
/// method emits the address of the lvalue, then loads the result as an rvalue,
/// returning the rvalue.
RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
+ // Load from __ptrauth.
+ if (auto ptrauth = LV.getQuals().getPointerAuth()) {
+ LV.getQuals().removePointerAuth();
+ auto value = EmitLoadOfLValue(LV, Loc).getScalarVal();
+ return RValue::get(EmitPointerAuthUnqualify(ptrauth, value, LV.getType(),
+ LV.getAddress(),
+ /*known nonnull*/ false));
+ }
+
if (LV.isObjCWeak()) {
// load of a __weak object.
Address AddrWeakObj = LV.getAddress();
@@ -2417,6 +2426,13 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
return EmitStoreThroughBitfieldLValue(Src, Dst);
}
+ // Handle __ptrauth qualification by re-signing the value.
+ if (auto pointerAuth = Dst.getQuals().getPointerAuth()) {
+ Src = RValue::get(EmitPointerAuthQualify(pointerAuth, Src.getScalarVal(),
+ Dst.getType(), Dst.getAddress(),
+ /*known nonnull*/ false));
+ }
+
// There's special magic for assigning into an ARC-qualified l-value.
if (Qualifiers::ObjCLifetime Lifetime = Dst.getQuals().getObjCLifetime()) {
switch (Lifetime) {
@@ -5555,6 +5571,27 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) {
return EmitCallee(ICE->getSubExpr());
}
+ // Try to remember the original __ptrauth qualifier for loads of
+ // function pointers.
+ if (ICE->getCastKind() == CK_LValueToRValue) {
+ auto subExpr = ICE->getSubExpr();
+ if (auto ptrType = subExpr->getType()->getAs<PointerType>()) {
+ auto result = EmitOrigPointerRValue(E);
+
+ QualType functionType = ptrType->getPointeeType();
+ assert(functionType->isFunctionType());
+
+ GlobalDecl GD;
+ if (const auto *VD =
+ dyn_cast_or_null<VarDecl>(E->getReferencedDeclOfCallee())) {
+ GD = GlobalDecl(VD);
+ }
+ CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), GD);
+ CGCallee callee(calleeInfo, result.first, result.second);
+ return callee;
+ }
+ }
+
// Resolve direct calls.
} else if (auto DRE = dyn_cast<DeclRefExpr>(E)) {
if (auto FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
@@ -5617,6 +5654,17 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
switch (getEvaluationKind(E->getType())) {
case TEK_Scalar: {
+ if (auto ptrauth = E->getLHS()->getType().getPointerAuth()) {
+ LValue LV = EmitCheckedLValue(E->getLHS(), TCK_Store);
+ LValue CopiedLV = LV;
+ CopiedLV.getQuals().removePointerAuth();
+ llvm::Value *RV =
+ EmitPointerAuthQualify(ptrauth, E->getRHS(), CopiedLV.getAddress());
+ EmitNullabilityCheck(CopiedLV, RV, E->getExprLoc());
+ EmitStoreThroughLValue(RValue::get(RV), CopiedLV);
+ return LV;
+ }
+
switch (E->getLHS()->getType().getObjCLifetime()) {
case Qualifiers::OCL_Strong:
return EmitARCStoreStrong(E, /*ignored*/ false).first;
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index f22321f0e738a..a66e937c9eabc 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1956,10 +1956,13 @@ namespace {
struct ConstantLValue {
llvm::Constant *Value;
bool HasOffsetApplied;
+ bool HasDestPointerAuth;
/*implicit*/ ConstantLValue(llvm::Constant *value,
- bool hasOffsetApplied = false)
- : Value(value), HasOffsetApplied(hasOffsetApplied) {}
+ bool hasOffsetApplied = false,
+ bool hasDestPointerAuth = false)
+ : Value(value), HasOffsetApplied(hasOffsetApplied),
+ HasDestPointerAuth(hasDestPointerAuth) {}
/*implicit*/ ConstantLValue(ConstantAddress address)
: ConstantLValue(address.getPointer()) {}
@@ -2064,6 +2067,15 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() {
value = applyOffset(value);
}
+ // Apply pointer-auth signing from the destination type.
+ if (auto pointerAuth = DestType.getPointerAuth()) {
+ if (!result.HasDestPointerAuth) {
+ value = Emitter.tryEmitConstantSignedPointer(value, pointerAuth);
+ if (!value)
+ return nullptr;
+ }
+ }
+
// Convert to the appropriate type; this could be an lvalue for
// an integer. FIXME: performAddrSpaceCast
if (isa<llvm::PointerType>(destTy))
@@ -2107,6 +2119,12 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
return CGM.GetWeakRefReference(D).getPointer();
auto PtrAuthSign = [&](llvm::Constant *C) {
+ if (auto pointerAuth = DestType.getPointerAuth()) {
+ C = applyOffset(C);
+ C = Emitter.tryEmitConstantSignedPointer(C, pointerAuth);
+ return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true);
+ }
+
CGPointerAuthInfo AuthInfo;
if (EnablePtrAuthFunctionTypeDiscrimination)
@@ -2120,7 +2138,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
C = CGM.getConstantSignedPointer(
C, AuthInfo.getKey(), nullptr,
cast_or_null<llvm::ConstantInt>(AuthInfo.getDiscriminator()));
- return ConstantLValue(C, /*applied offset*/ true);
+ return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true);
}
return ConstantLValue(C);
@@ -2334,6 +2352,9 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
EnablePtrAuthFunctionTypeDiscrimination)
.tryEmit();
case APValue::Int:
+ if (DestType.getPointerAuth() && Value.getInt() != 0) {
+ return nullptr;
+ }
return llvm::ConstantInt::get(CGM.getLLVMContext(), Value.getInt());
case APValue::FixedPoint:
return llvm::ConstantInt::get(CGM.getLLVMContext(),
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index a17d68424bbce..05c76cf85581c 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2196,6 +2196,58 @@ Value *ScalarExprEmitter::VisitInitListExpr(InitListExpr *E) {
return V;
}
+static bool isDeclRefKnownNonNull(CodeGenFunction &CGF, const ValueDecl *D) {
+ return !D->isWeak();
+}
+
+static bool isLValueKnownNonNull(CodeGenFunction &CGF, const Expr *E) {
+ E = E->IgnoreParens();
+
+ if (auto UO = dyn_cast<UnaryOperator>(E)) {
+ if (UO->getOpcode() == UO_Deref) {
+ return CGF.isPointerKnownNonNull(UO->getSubExpr());
+ }
+ }
+
+ if (auto DRE = dyn_cast<DeclRefExpr>(E)) {
+ return isDeclRefKnownNonNull(CGF, DRE->getDecl());
+ } else if (auto ME = dyn_cast<MemberExpr>(E)) {
+ if (isa<FieldDecl>(ME->getMemberDecl()))
+ return true;
+ return isDeclRefKnownNonNull(CGF, ME->getMemberDecl());
+ }
+
+ // Array subscripts? Anything else?
+
+ return false;
+}
+
+bool CodeGenFunction::isPointerKnownNonNull(const Expr *E) {
+ assert(E->getType()->isSignableType());
+
+ E = E->IgnoreParens();
+
+ if (isa<CXXThisExpr>(E))
+ return true;
+
+ if (auto UO = dyn_cast<UnaryOperator>(E)) {
+ if (UO->getOpcode() == UO_AddrOf) {
+ return isLValueKnownNonNull(*this, UO->getSubExpr());
+ }
+ }
+
+ if (auto CE = dyn_cast<CastExpr>(E)) {
+ if (CE->getCastKind() == CK_FunctionToPointerDecay ||
+ CE->getCastKind() == CK_ArrayToPointerDecay) {
+ return isLValueKnownNonNull(*this, CE->getSubExpr());
+ }
+ }
+
+ // Maybe honor __nonnull?
+
+ return false;
+}
+
bool CodeGenFunction::ShouldNullCheckClassCastValue(const CastExpr *CE) {
const Expr *E = CE->getSubExpr();
@@ -4798,6 +4850,21 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
Value *RHS;
LValue LHS;
+ if (auto ptrauth = E->getLHS()->getType().getPointerAuth()) {
+ LValue LV = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store);
+ LV.getQuals().removePointerAuth();
+ llvm::Value *RV =
+ CGF.EmitPointerAuthQualify(ptrauth, E->getRHS(), LV.getAddress());
+ CGF.EmitNullabilityCheck(LV, RV, E->getExprLoc());
+ CGF.EmitStoreThroughLValue(RValue::get(RV), LV);
+
+ if (Ignore)
+ return nullptr;
+ RV = CGF.EmitPointerAuthUnqualify(ptrauth, RV, LV.getType(),
+ LV.getAddress(), /*nonnull*/ false);
+ return RV;
+ }
+
switch (E->getLHS()->getType().getObjCLifetime()) {
case Qualifiers::OCL_Strong:
std::tie(LHS, RHS) = CGF.EmitARCStoreStrong(E, Ignore);
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp
index 0c63b9d6bb7e7..5cd9cba6b41d1 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -125,6 +125,33 @@ CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo(
Schema.authenticatesNullValues(), Discriminator);
}
+CGPointerAuthInfo
+CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier Qual,
+ Address StorageAddress) {
+ assert(Qual && "don't call this if you don't know that the Qual is present");
+ if (Qual.hasKeyNone())
+ return CGPointerAuthInfo();
+
+ llvm::Value *Discriminator = nullptr;
+ if (unsigned Extra = Qual.getExtraDiscriminator())
+ Discriminator = llvm::ConstantInt::get(IntPtrTy, Extra);
+
+ if (Qual.isAddressDiscriminated()) {
+ assert(StorageAddress.isValid() &&
+ "address discrimination without address");
+ llvm::Value *StoragePtr = StorageAddress.emitRawPointer(*this);
+ if (Discriminator)
+ Discriminator =
+ EmitPointerAuthBlendDiscriminator(StoragePtr, Discriminator);
+ else
+ Discriminator = Builder.CreatePtrToInt(StoragePtr, IntPtrTy);
+ }
+
+ return CGPointerAuthInfo(Qual.getKey(), Qual.getAuthenticationMode(),
+ Qual.isIsaPointer(), Qual.authenticatesNullValues(),
+ Discriminator);
+}
+
/// Return the natural pointer authentication for values of the given
/// pointee type.
static CGPointerAuthInfo
@@ -166,6 +193,102 @@ CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) {
return ::getPointerAuthInfoForType(*this, T);
}
+static std::pair<llvm::Value *, CGPointerAuthInfo>
+emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &lv,
+ SourceLocation loc) {
+ auto value = CGF.EmitLoadOfScalar(lv, loc);
+ CGPointerAuthInfo authInfo;
+ if (auto ptrauth = lv.getQuals().getPointerAuth()) {
+ authInfo = CGF.EmitPointerAuthInfo(ptrauth, lv.getAddress());
+ } else {
+ authInfo = getPointerAuthInfoForType(CGF.CGM, lv.getType());
+ }
+ return {value, authInfo};
+}
+
+std::pair<llvm::Value *, CGPointerAuthInfo>
+CodeGenFunction::EmitOrigPointerRValue(const Expr *E) {
+ assert(E->getType()->isSignableType());
+
+ E = E->IgnoreParens();
+ if (auto load = dyn_cast<ImplicitCastExpr>(E)) {
+ if (load->getCastKind() == CK_LValueToRValue) {
+ E = load->getSubExpr()->IgnoreParens();
+
+ // We're semantically required to not emit loads of certain DREs naively.
+ if (auto refExpr = dyn_cast<DeclRefExpr>(const_cast<Expr *>(E))) {
+ if (auto result = tryEmitAsConstant(refExpr)) {
+ // Fold away a use of an intermediate variable.
+ if (!result.isReference())
+ return {result.getValue(),
+ getPointerAuthInfoForType(CGM, refExpr->getType())};
+
+ // Fold away a use of an intermediate reference.
+ auto lv = result.getReferenceLValue(*this, refExpr);
+ return emitLoadOfOrigPointerRValue(*this, lv, refExpr->getLocation());
+ }
+ }
+
+ // Otherwise, load and use the pointer
+ auto lv = EmitCheckedLValue(E, CodeGenFunction::TCK_Load);
+ return emitLoadOfOrigPointerRValue(*this, lv, E->getExprLoc());
+ }
+ }
+
+ // Emit direct references to functions without authentication.
+ if (auto DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (auto FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
+ return {CGM.getRawFunctionPointer(FD), CGPointerAuthInfo()};
+ }
+ } else if (auto ME = dyn_cast<MemberExpr>(E)) {
+ if (auto FD = dyn_cast<FunctionDecl>(ME->getMemberDecl())) {
+ EmitIgnoredExpr(ME->getBase());
+ return {CGM.getRawFunctionPointer(FD), CGPointerAuthInfo()};
+ }
+ }
+
+ // Fallback: just use the normal rules for the type.
+ auto value = EmitScalarExpr(E);
+ return {value, getPointerAuthInfoForType(CGM, E->getType())};
+}
+
+llvm::Value *
+CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier,
+ const Expr *E,
+ Address destStorageAddress) {
+ assert(destQualifier);
+
+ auto src = EmitOrigPointerRValue(E);
+ auto value = src.first;
+ auto curAuthInfo = src.second;
+
+ auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress);
+ return emitPointerAuthResign(value, E->getType(), curAuthInfo, destAuthInfo,
+ isPointerKnownNonNull(E));
+}
+
+llvm::Value *CodeGenFunction::EmitPointerAuthQualify(
+ PointerAuthQualifier destQualifier, llvm::Value *value,
+ QualType pointerType, Address destStorageAddress, bool isKnownNonNull) {
+ assert(destQualifier);
+
+ auto curAuthInfo = getPointerAuthInfoForType(CGM, pointerType);
+ auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress);
+ return emitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo,
+ isKnownNonNull);
+}
+
+llvm::Value *CodeGenFunction::EmitPointerAuthUnqualify(
+ PointerAuthQualifier curQualifier, llvm::Value *value, QualType pointerType,
+ Address curStorageAddress, bool isKnownNonNull) {
+ assert(curQualifier);
+
+ auto curAuthInfo = EmitPointerAuthInfo(curQualifier, curStorageAddress);
+ auto destAuthInfo = getPointerAuthInfoForType(CGM, pointerType);
+ return emitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo,
+ isKnownNonNull);
+}
+
static bool isZeroConstant(const llvm::Value *Value) {
if (const auto *CI = dyn_cast<llvm::ConstantInt>(Value))
return CI->isZero();
@@ -288,6 +411,23 @@ llvm::Value *CodeGenFunction::emitPointerAuthResign(
return Value;
}
+void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier Qual, QualType T,
+ Address DestAddress,
+ Address SrcAddress) {
+ assert(Qual);
+ llvm::Value *Value = Builder.CreateLoad(SrcAddress);
+
+ // If we're using address-discrimination, we have to re-sign the value.
+ if (Qual.isAddressDiscriminated()) {
+ CGPointerAuthInfo SrcPtrAuth = EmitPointerAuthInfo(Qual, SrcAddress);
+ CGPointerAuthInfo DestPtrAuth = EmitPointerAuthInfo(Qual, DestAddress);
+ Value = emitPointerAuthResign(Value, T, SrcPtrAuth, DestPtrAuth,
+ /*IsKnownNonNull=*/false);
+ }
+
+ Builder.CreateStore(Value, DestAddress);
+}
+
llvm::Constant *
CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
llvm::Constant *StorageAddress,
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 67e3019565cd0..e7729207cc6a6 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4457,6 +4457,26 @@ class CodeGenFunction : public CodeGenTypeCache {
const CGPointerAuthInfo &Info,
SmallVectorImpl<llvm::OperandBundleDef> &Bundles);
+ CGPointerAuthInfo EmitPointerAuthInfo(PointerAuthQualifier qualifier,
+ Address storageAddress);
+ llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier qualifier,
+ llvm::Value *pointer, QualType valueType,
+ Address storageAddress,
+ bool isKnownNonNull);
+ llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier qualifier,
+ const Expr *pointerExpr,
+ Address storageAddress);
+ llvm::Value *EmitPointerAuthUnqualify(PointerAuthQualifier qualifier,
+ llvm::Value *pointer,
+ QualType pointerType,
+ Address storageAddress,
+ bool isKnownNonNull);
+ void EmitPointerAuthCopy(PointerAuthQualifier qualifier, QualType type,
+ Address destField, Address srcField);
+
+ std::pair<llvm::Value *, CGPointerAuthInfo>
+ EmitOrigPointerRValue(const Expr *E);
+
llvm::Value *authPointerToPointerCast(llvm::Value *ResultPtr,
QualType SourceType, QualType DestType);
Address authPointerToPointerCast(Address Ptr, QualType SourceType,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7ce9a9cea1c7a..2a14a6a34eede 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3366,6 +3366,40 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
}
}
+/// type-qualifier:
+/// ('__ptrauth') '(' constant-expression
+/// (',' constant-expression)[opt]
+/// (',' constant-expression)[opt] ')'
+void Parser::ParsePtrauthQualifier(ParsedAttributes &attrs) {
+ assert(Tok.is(tok::kw___ptrauth));
+
+ IdentifierInfo *kwName = Tok.getIdentifierInfo();
+ SourceLocation kwLoc = ConsumeToken();
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume())
+ return;
+
+ ArgsVector argExprs;
+ do {
+ ExprResult expr = ParseAssignmentExpression();
+ if (expr.isInvalid()) {
+ T.skipToEnd();
+ return;
+ }
+ argExprs.push_back(expr.get());
+ } while (TryConsumeToken(tok::comma));
+
+ T.consumeClose();
+ SourceLocation endLoc = T.getCloseLocation();
+
+ attrs.addNew(kwName, SourceRange(kwLoc, endLoc),
+ /*scope*/ nullptr, SourceLocation(), argExprs.data(),
+ argExprs.size(),
+ ParsedAttr::Form::Keyword(/*IsAlignAs=*/false,
+ /*IsRegularKeywordAttribute=*/false));
+}
+
/// Bounds attributes (e.g., counted_by):
/// AttrName '(' expression ')'
void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
@@ -4227,6 +4261,11 @@ void Parser::ParseDeclarationSpecifiers(
getLangOpts());
break;
+ // __ptrauth qualifier.
+ case tok::kw___ptrauth:
+ ParsePtrauthQualifier(DS.getAttributes());
+ continue;
+
case tok::kw___sptr:
case tok::kw___uptr:
case tok::kw___ptr64:
@@ -5924,6 +5963,7 @@ bool Parser::isTypeSpecifierQualifier() {
case tok::kw___ptr32:
case tok::kw___pascal:
case tok::kw___unaligned:
+ case tok::kw___ptrauth:
case tok::kw__Nonnull:
case tok::kw__Nullable:
@@ -6213,6 +6253,7 @@ bool Parser::isDeclarationSpecifier(
case tok::kw___forceinline:
case tok::kw___pascal:
case tok::kw___unaligned:
+ case tok::kw___ptrauth:
case tok::kw__Nonnull:
case tok::kw__Nullable:
@@ -6475,6 +6516,12 @@ void Parser::ParseTypeQualifierListOpt(
ParseHLSLQualifiers(DS.getAttributes());
continue;
+ // __ptrauth qualifier.
+ case tok::kw___ptrauth:
+ ParsePtrauthQualifier(DS.getAttributes());
+ EndLoc = PrevTokLocation;
+ continue;
+
case tok::kw___unaligned:
isInvalid = DS.SetTypeQual(DeclSpec::TQ_unaligned, Loc, PrevSpec, DiagID,
getLangOpts());
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index eca8363ee9605..d4372c3470784 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -73,7 +73,7 @@ namespace {
// value of the expression to the unqualified, non-atomic version of
// the named type.
if (!S.Context.getLangOpts().ObjC && !DestType->isRecordType() &&
- !DestType->isArrayType()) {
+ !DestType->isArrayType() && !DestType.getPointerAuth()) {
DestType = DestType.getAtomicUnqualifiedType();
}
@@ -167,6 +167,14 @@ namespace {
SrcExpr = src;
}
+ void checkQualifiedDestType() {
+ // Destination type may not be qualified with __ptrauth.
+ if (DestType.getPointerAuth()) {
+ Self.Diag(DestRange.getBegin(), diag::err_ptrauth_qualifier_cast)
+ << DestType << DestRange;
+ }
+ }
+
/// Check for and handle non-overload placeholder expressions.
void checkNonOverloadPlaceholders() {
if (!isPlaceholder() || isPlaceholder(BuiltinType::Overload))
@@ -308,6 +316,8 @@ Sema::BuildCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind,
Op.OpRange = SourceRange(OpLoc, Parens.getEnd());
Op.DestRange = AngleBrackets;
+ Op.checkQualifiedDestType();
+
switch (Kind) {
default: llvm_unreachable("Unknown C++ cast!");
@@ -3359,6 +3369,8 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc,
// -Wcast-qual
DiagnoseCastQual(Op.Self, Op.SrcExpr, Op.DestType);
+ Op.checkQualifiedDestType();
+
return Op.complete(CStyleCastExpr::Create(
Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(),
&Op.BasePath, CurFPFeatureOverrides(), CastTypeInfo, LPLoc, RPLoc));
@@ -3378,6 +3390,8 @@ ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo,
if (Op.SrcExpr.isInvalid())
return ExprError();
+ Op.checkQualifiedDestType();
+
auto *SubExpr = Op.SrcExpr.get();
if (auto *BindExpr = dyn_cast<CXXBindTemporaryExpr>(SubExpr))
SubExpr = BindExpr->getSubExpr();
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index bde92e04b6b83..4aefc12789c13 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1540,6 +1540,54 @@ bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) {
return false;
}
+bool Sema::checkPointerAuthDiscriminatorArg(Expr *arg,
+ PointerAuthDiscArgKind kind,
+ unsigned &intVal) {
+ if (!arg) {
+ intVal = 0;
+ return true;
+ }
+
+ std::optional<llvm::APSInt> result = arg->getIntegerConstantExpr(Context);
+ if (!result) {
+ Diag(arg->getExprLoc(), diag::err_ptrauth_arg_not_ice);
+ return false;
+ }
+
+ unsigned max;
+ bool isAddrDiscArg = false;
+
+ switch (kind) {
+ case PADAK_AddrDiscPtrAuth:
+ max = 1;
+ isAddrDiscArg = true;
+ break;
+ case PADAK_ExtraDiscPtrAuth:
+ max = PointerAuthQualifier::MaxDiscriminator;
+ break;
+ };
+
+ if (*result < 0 || *result > max) {
+ llvm::SmallString<32> value;
+ {
+ llvm::raw_svector_ostream str(value);
+ str << *result;
+ }
+
+ if (isAddrDiscArg)
+ Diag(arg->getExprLoc(), diag::err_ptrauth_address_discrimination_invalid)
+ << value;
+ else
+ Diag(arg->getExprLoc(), diag::err_ptrauth_extra_discriminator_invalid)
+ << value << max;
+
+ return false;
+ };
+
+ intVal = result->getZExtValue();
+ return true;
+}
+
static std::pair<const ValueDecl *, CharUnits>
findConstantBaseAndOffset(Sema &S, Expr *E) {
// Must evaluate as a pointer.
@@ -3795,6 +3843,14 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
return ExprError();
}
+ auto PointerAuth = AtomTy.getPointerAuth();
+ if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
+ Diag(ExprRange.getBegin(),
+ diag::err_atomic_op_needs_non_address_discriminated_pointer)
+ << 0 << Ptr->getType() << Ptr->getSourceRange();
+ return ExprError();
+ }
+
// For an arithmetic operation, the implied arithmetic must be well-formed.
if (Form == Arithmetic) {
// GCC does not enforce these rules for GNU atomics, but we do to help catch
@@ -4161,6 +4217,13 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
<< FirstArg->getType() << 0 << FirstArg->getSourceRange();
return ExprError();
}
+ auto pointerAuth = ValType.getPointerAuth();
+ if (pointerAuth && pointerAuth.isAddressDiscriminated()) {
+ Diag(FirstArg->getBeginLoc(),
+ diag::err_atomic_op_needs_non_address_discriminated_pointer)
+ << 1 << ValType << FirstArg->getSourceRange();
+ return ExprError();
+ }
if (ValType.isConstQualified()) {
Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_cannot_be_const)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 575bd292f27de..cbd574654a0ff 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15099,6 +15099,13 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
New->setType(T);
}
+ // __ptrauth is forbidden on parameters.
+ if (T.getPointerAuth()) {
+ Diag(NameLoc, diag::err_ptrauth_qualifier_invalid)
+ << T << (int)!T->isSignableType() << 1;
+ New->setInvalidDecl();
+ }
+
// ISO/IEC TR 18037 S6.7.3: "The type of an object with automatic storage
// duration shall not be qualified by an address-space qualifier."
// Since all parameters have automatic store duration, they can not have
@@ -19033,9 +19040,14 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
RecordArgPassingKind::CanNeverPassInRegs)
Record->setArgPassingRestrictions(
RecordArgPassingKind::CanNeverPassInRegs);
- } else if (FT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak)
+ } else if (FT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak) {
Record->setArgPassingRestrictions(
RecordArgPassingKind::CanNeverPassInRegs);
+ } else if (PointerAuthQualifier Q = FT.getPointerAuth()) {
+ if (Q.isAddressDiscriminated())
+ Record->setArgPassingRestrictions(
+ RecordArgPassingKind::CanNeverPassInRegs);
+ }
}
if (Record && FD->getType().isVolatileQualified())
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1cca8ac9b9343..1b820c7c88522 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9250,6 +9250,8 @@ struct SpecialMemberDeletionInfo
bool shouldDeleteForVariantObjCPtrMember(FieldDecl *FD, QualType FieldType);
+ bool shouldDeleteForVariantPtrAuthMember(FieldDecl *FD, QualType FieldType);
+
bool visitBase(CXXBaseSpecifier *Base) { return shouldDeleteForBase(Base); }
bool visitField(FieldDecl *Field) { return shouldDeleteForField(Field); }
@@ -9418,6 +9420,29 @@ bool SpecialMemberDeletionInfo::shouldDeleteForVariantObjCPtrMember(
return true;
}
+bool SpecialMemberDeletionInfo::shouldDeleteForVariantPtrAuthMember(
+ FieldDecl *FD, QualType FieldType) {
+ // Copy/move constructors/assignment operators are deleted if the field has an
+ // address-discriminated ptrauth qualifier.
+ PointerAuthQualifier Q = FieldType.getPointerAuth();
+
+ if (!Q || !Q.isAddressDiscriminated())
+ return false;
+
+ if (CSM == CXXSpecialMemberKind::DefaultConstructor ||
+ CSM == CXXSpecialMemberKind::Destructor)
+ return false;
+
+ if (Diagnose) {
+ auto *ParentClass = cast<CXXRecordDecl>(FD->getParent());
+ S.Diag(FD->getLocation(), diag::note_deleted_special_member_class_subobject)
+ << llvm::to_underlying(getEffectiveCSM()) << ParentClass
+ << /*IsField*/ true << FD << 4 << /*IsDtorCallInCtor*/ false << 2;
+ }
+
+ return true;
+}
+
/// Check whether we should delete a special member function due to the class
/// having a particular direct or virtual base class.
bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) {
@@ -9456,6 +9481,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
if (inUnion() && shouldDeleteForVariantObjCPtrMember(FD, FieldType))
return true;
+ if (inUnion() && shouldDeleteForVariantPtrAuthMember(FD, FieldType))
+ return true;
+
if (CSM == CXXSpecialMemberKind::DefaultConstructor) {
// For a default constructor, all references must be initialized in-class
// and, if a union, it must have a non-const member.
@@ -9519,6 +9547,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
if (shouldDeleteForVariantObjCPtrMember(&*UI, UnionFieldType))
return true;
+ if (shouldDeleteForVariantPtrAuthMember(&*UI, UnionFieldType))
+ return true;
+
if (!UnionFieldType.isConstQualified())
AllVariantFieldsAreConst = false;
@@ -10363,6 +10394,12 @@ void Sema::checkIllFormedTrivialABIStruct(CXXRecordDecl &RD) {
return;
}
+ // Ill-formed if the field is an address-discriminated pointer.
+ if (FT.hasAddressDiscriminatedPointerAuth()) {
+ PrintDiagAndRemoveAttr(6);
+ return;
+ }
+
if (const auto *RT = FT->getBaseElementTypeUnsafe()->getAs<RecordType>())
if (!RT->isDependentType() &&
!cast<CXXRecordDecl>(RT->getDecl())->canPassInRegisters()) {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 74c0e01705905..d854cbdd8eb11 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7957,6 +7957,13 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS,
lhQual.removeCVRQualifiers();
rhQual.removeCVRQualifiers();
+ if (!lhQual.getPointerAuth().isEquivalent(rhQual.getPointerAuth())) {
+ S.Diag(Loc, diag::err_typecheck_cond_incompatible_ptrauth)
+ << LHSTy << RHSTy << LHS.get()->getSourceRange()
+ << RHS.get()->getSourceRange();
+ return QualType();
+ }
+
// OpenCL v2.0 specification doesn't extend compatibility of type qualifiers
// (C99 6.7.3) for address spaces. We assume that the check should behave in
// the same manner as it's defined for CVR qualifiers, so for OpenCL two
@@ -8873,6 +8880,10 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType,
else if (lhq.getObjCLifetime() != rhq.getObjCLifetime())
ConvTy = Sema::IncompatiblePointerDiscardsQualifiers;
+ // Treat pointer-auth mismatches as fatal.
+ else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth()))
+ ConvTy = Sema::IncompatiblePointerDiscardsQualifiers;
+
// For GCC/MS compatibility, other qualifier mismatches are treated
// as still compatible in C.
else ConvTy = Sema::CompatiblePointerDiscardsQualifiers;
@@ -16689,6 +16700,9 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
} else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) {
DiagKind = diag::err_typecheck_incompatible_ownership;
break;
+ } else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth())) {
+ DiagKind = diag::err_typecheck_incompatible_ptrauth;
+ break;
}
llvm_unreachable("unknown error case for discarding qualifiers!");
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 51c4a36808fce..763653e81c5b4 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7266,6 +7266,11 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
else
return QualType();
+ if (Q1.getPointerAuth().isEquivalent(Q2.getPointerAuth()))
+ Quals.setPointerAuth(Q1.getPointerAuth());
+ else
+ return QualType();
+
Steps.back().Quals = Quals;
if (Q1 != Quals || Q2 != Quals)
NeedConstBefore = Steps.size() - 1;
diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp
index 031f2a6af8774..246f19840a795 100644
--- a/clang/lib/Sema/SemaObjCProperty.cpp
+++ b/clang/lib/Sema/SemaObjCProperty.cpp
@@ -182,6 +182,10 @@ Decl *SemaObjC::ActOnProperty(Scope *S, SourceLocation AtLoc,
0);
TypeSourceInfo *TSI = SemaRef.GetTypeForDeclarator(FD.D);
QualType T = TSI->getType();
+ if (T.getPointerAuth().isPresent()) {
+ Diag(AtLoc, diag::err_ptrauth_qualifier_invalid)
+ << T << (int)!T->isSignableType() << 2;
+ }
if (!getOwnershipRule(Attributes)) {
Attributes |= deducePropertyOwnershipFromType(SemaRef, T);
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 86074a4f3a585..521d14ba1f84b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -11222,6 +11222,17 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand,
return;
}
+ if (!FromQs.getPointerAuth().isEquivalent(ToQs.getPointerAuth())) {
+ S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_ptrauth)
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << FromTy << !!FromQs.getPointerAuth()
+ << FromQs.getPointerAuth().getAsString() << !!ToQs.getPointerAuth()
+ << ToQs.getPointerAuth().getAsString() << I + 1
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange());
+ MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
+ return;
+ }
+
unsigned CVR = FromQs.getCVRQualifiers() & ~ToQs.getCVRQualifiers();
assert(CVR && "expected qualifiers mismatch");
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 6fa39cdccef2b..c4da0f237c833 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -2522,6 +2522,13 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) {
return true;
}
+ // __ptrauth is illegal on a function return type.
+ if (T.getPointerAuth()) {
+ Diag(Loc, diag::err_ptrauth_qualifier_invalid)
+ << T << (int)!T->isSignableType() << 0;
+ return true;
+ }
+
if (T.hasNonTrivialToPrimitiveDestructCUnion() ||
T.hasNonTrivialToPrimitiveCopyCUnion())
checkNonTrivialCUnion(T, Loc, NTCUC_FunctionReturn,
@@ -2625,6 +2632,11 @@ QualType Sema::BuildFunctionType(QualType T,
} else if (ParamType->isWebAssemblyTableType()) {
Diag(Loc, diag::err_wasm_table_as_function_parameter);
Invalid = true;
+ } else if (ParamType.getPointerAuth()) {
+ // __ptrauth is illegal on a function return type.
+ Diag(Loc, diag::err_ptrauth_qualifier_invalid)
+ << T << (int)!T->isSignableType() << 1;
+ Invalid = true;
}
// C++2a [dcl.fct]p4:
@@ -4923,6 +4935,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
}
}
+ // __ptrauth is illegal on a function return type.
+ if (T.getPointerAuth()) {
+ S.Diag(DeclType.Loc, diag::err_ptrauth_qualifier_invalid)
+ << T << (int)!T->isSignableType() << 0;
+ }
+
if (LangOpts.OpenCL) {
// OpenCL v2.0 s6.12.5 - A block cannot be the return value of a
// function.
@@ -8210,6 +8228,70 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr,
CurType = S.Context.getVectorType(CurType, numElts, VecKind);
}
+/// Handle the __ptrauth qualifier.
+static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &type,
+ const ParsedAttr &attr, Sema &S) {
+ auto attributeName = attr.getAttrName()->getName();
+ if (attr.getNumArgs() < 1 || attr.getNumArgs() > 3) {
+ S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_bad_arg_count)
+ << attributeName;
+ attr.setInvalid();
+ return;
+ }
+
+ Expr *keyArg = attr.getArgAsExpr(0);
+ Expr *isAddressDiscriminatedArg =
+ attr.getNumArgs() >= 2 ? attr.getArgAsExpr(1) : nullptr;
+ Expr *extraDiscriminatorArg =
+ attr.getNumArgs() >= 3 ? attr.getArgAsExpr(2) : nullptr;
+
+ unsigned key;
+ if (S.checkConstantPointerAuthKey(keyArg, key)) {
+ attr.setInvalid();
+ return;
+ }
+ assert(key <= PointerAuthQualifier::MaxKey && "ptrauth key is out of range");
+
+ bool isInvalid = false;
+ unsigned isAddressDiscriminated, extraDiscriminator;
+ isInvalid |= !S.checkPointerAuthDiscriminatorArg(isAddressDiscriminatedArg,
+ Sema::PADAK_AddrDiscPtrAuth,
+ isAddressDiscriminated);
+ isInvalid |= !S.checkPointerAuthDiscriminatorArg(
+ extraDiscriminatorArg, Sema::PADAK_ExtraDiscPtrAuth, extraDiscriminator);
+
+ if (isInvalid) {
+ attr.setInvalid();
+ return;
+ }
+
+ if (!type->isSignableType() && !type->isDependentType()) {
+ S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << type;
+ attr.setInvalid();
+ return;
+ }
+
+ if (type.getPointerAuth()) {
+ S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_redundant)
+ << type << attr.getAttrName()->getName();
+ attr.setInvalid();
+ return;
+ }
+
+ if (!S.getLangOpts().PointerAuthIntrinsics) {
+ S.Diag(attr.getLoc(), diag::err_ptrauth_disabled) << attr.getRange();
+ attr.setInvalid();
+ return;
+ }
+
+ assert((!isAddressDiscriminatedArg || isAddressDiscriminated <= 1) &&
+ "address discriminator arg should be either 0 or 1");
+ PointerAuthQualifier qual = PointerAuthQualifier::Create(
+ key, isAddressDiscriminated, extraDiscriminator,
+ PointerAuthenticationMode::SignAndAuth, false, false);
+ type = S.Context.getPointerAuthType(type, qual);
+}
+
/// HandleArmSveVectorBitsTypeAttr - The "arm_sve_vector_bits" attribute is
/// used to create fixed-length versions of sizeless SVE types defined by
/// the ACLE, such as svint32_t and svbool_t.
@@ -8634,6 +8716,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
HandleOpenCLAccessAttr(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
+ case ParsedAttr::AT_PointerAuth:
+ HandlePtrAuthQualifier(state.getSema().Context, type, attr,
+ state.getSema());
+ attr.setUsedAsTypeAttr();
+ break;
case ParsedAttr::AT_LifetimeBound:
if (TAL == TAL_DeclChunk)
HandleLifetimeBoundAttr(state, type, attr);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4d68ebf0cc452..35bc8d50dc901 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5097,6 +5097,17 @@ QualType TreeTransform<Derived>::RebuildQualifiedType(QualType T,
return QualType();
}
+ auto LocalPointerAuth = Quals.getPointerAuth();
+ if (LocalPointerAuth.isPresent()) {
+ if (T.getPointerAuth().isPresent()) {
+ SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_redundant)
+ << TL.getType() << "__ptrauth";
+ return QualType();
+ } else if (!T->isSignableType() && !T->isDependentType()) {
+ SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_nonpointer) << T;
+ return QualType();
+ }
+ }
// C++ [dcl.fct]p7:
// [When] adding cv-qualifications on top of the function type [...] the
// cv-qualifiers are ignored.
diff --git a/clang/test/CodeGen/ptrauth-debuginfo.c b/clang/test/CodeGen/ptrauth-debuginfo.c
new file mode 100644
index 0000000000000..214ecafca303c
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-debuginfo.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios \
+// RUN: -fptrauth-calls -fptrauth-intrinsics -emit-llvm -fblocks \
+// RUN: %s -debug-info-kind=limited -o - | FileCheck %s
+
+// Constant initializers for data pointers.
+extern int external_int;
+
+int *__ptrauth(1, 0, 1234) g1 = &external_int;
+// CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type,
+// CHECK-SAME: ptrAuthKey: 1,
+// CHECK-SAME: ptrAuthIsAddressDiscriminated: false,
+// CHECK-SAME: ptrAuthExtraDiscriminator: 1234,
+// CHECK-SAME: ptrAuthIsaPointer: false,
+// CHECK-SAME: ptrAuthAuthenticatesNullValues: false)
+
+struct A {
+ int value;
+};
+struct A *createA(void);
+
+void f() {
+ __block struct A *__ptrauth(1, 1, 1236) ptr = createA();
+ ^{
+ (void)ptr->value;
+ }();
+}
+// CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type,
+// CHECK-SAME: ptrAuthKey: 1,
+// CHECK-SAME: ptrAuthIsAddressDiscriminated: true,
+// CHECK-SAME: ptrAuthExtraDiscriminator: 1236,
+// CHECK-SAME: ptrAuthIsaPointer: false,
+// CHECK-SAME: ptrAuthAuthenticatesNullValues: false)
diff --git a/clang/test/CodeGen/ptrauth-qualifier-function.c b/clang/test/CodeGen/ptrauth-qualifier-function.c
new file mode 100644
index 0000000000000..f6cde0cd96e62
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-qualifier-function.c
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s
+// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void (*fptr)(void);
+void (* __ptrauth(0, 0, 42) f2ptr_42_discm)(int);
+
+// CHECK-LABEL: define void @test_assign_to_qualified
+void test_assign_to_qualified() {
+ f2ptr_42_discm = (void (*)(int))fptr;
+
+ // CHECK: [[ENTRY:.*]]:{{$}}
+ // CHECK: [[FPTR:%.*]] = load ptr, ptr @fptr
+ // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR]], null
+ // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN1:.*]], label %[[JOIN1:.*]]
+
+ // CHECK: [[RESIGN1]]:
+ // CHECK-NEXT: [[FPTR2:%.*]] = ptrtoint ptr [[FPTR]] to i64
+ // CHECK-NEXT: [[FPTR4:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR2]], i32 0, i64 18983, i32 0, i64 2712)
+ // CHECK-NEXT: [[FPTR5:%.*]] = inttoptr i64 [[FPTR4]] to ptr
+ // CHECK-NEXT: br label %[[JOIN1]]
+
+ // CHECK: [[JOIN1]]:
+ // CHECK-NEXT: [[FPTR6:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[FPTR5]], %[[RESIGN1]] ]
+ // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR6]], null
+ // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN2:.*]], label %[[JOIN2:.*]]
+
+ // CHECK: [[RESIGN2]]:
+ // CHECK-NEXT: [[FPTR7:%.*]] = ptrtoint ptr [[FPTR6]] to i64
+ // CHECK-NEXT: [[FPTR8:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR7]], i32 0, i64 2712, i32 0, i64 42)
+ // CHECK-NEXT: [[FPTR9:%.*]] = inttoptr i64 [[FPTR8]] to ptr
+ // CHECK-NEXT: br label %[[JOIN2]]
+
+ // CHECK: [[JOIN2]]
+ // CHECK-NEXT: [[FPTR10:%.*]] = phi ptr [ null, %[[JOIN1]] ], [ [[FPTR9]], %[[RESIGN2]] ]
+ // CHECK-NEXT store void (i32)* [[FPTR10]], void (i32)** @f2ptr_42_discm
+}
+
+// CHECK-LABEL: define void @test_assign_from_qualified
+void test_assign_from_qualified() {
+ fptr = (void (*)(void))f2ptr_42_discm;
+
+ // CHECK: [[ENTRY:.*]]:{{$}}
+ // CHECK: [[FPTR:%.*]] = load ptr, ptr @f2ptr_42_discm
+ // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR]], null
+ // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN1:.*]], label %[[JOIN1:.*]]
+
+ // CHECK: [[RESIGN1]]:
+ // CHECK-NEXT: [[FPTR1:%.*]] = ptrtoint ptr [[FPTR]] to i64
+ // CHECK-NEXT: [[FPTR2:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR1]], i32 0, i64 42, i32 0, i64 2712)
+ // CHECK-NEXT: [[FPTR3:%.*]] = inttoptr i64 [[FPTR2]] to ptr
+ // CHECK-NEXT: br label %[[JOIN1]]
+
+ // CHECK: [[JOIN1]]:
+ // CHECK-NEXT: [[FPTR4:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[FPTR3]], %[[RESIGN1]] ]
+ // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR4]], null
+ // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN2:.*]], label %[[JOIN2:.*]]
+
+ // CHECK: [[RESIGN2]]:
+ // CHECK-NEXT: [[FPTR6:%.*]] = ptrtoint ptr [[FPTR4]] to i64
+ // CHECK-NEXT: [[FPTR7:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR6]], i32 0, i64 2712, i32 0, i64 18983)
+ // CHECK-NEXT: [[FPTR8:%.*]] = inttoptr i64 [[FPTR7]] to ptr
+ // CHECK-NEXT: br label %[[JOIN2]]
+
+ // CHECK: [[JOIN2]]
+ // CHECK-NEXT: [[FPTR9:%.*]] = phi ptr [ null, %[[JOIN1]] ], [ [[FPTR8]], %[[RESIGN2]] ]
+ // CHECK-NEXT store void ()* [[FPTR10]], void ()** @f2ptr_42_discm
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/clang/test/CodeGen/ptrauth-qualifier-loadstore.c b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c
new file mode 100644
index 0000000000000..41a87fba58e17
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c
@@ -0,0 +1,744 @@
+// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s
+
+#define IQ __ptrauth(1,0,50)
+#define AQ __ptrauth(1,1,50)
+#define DIFF_IQ __ptrauth(1,0,100)
+#define DIFF_AQ __ptrauth(1,1,100)
+#define ZERO_IQ __ptrauth(1,0,0)
+#define ZERO_AQ __ptrauth(1,1,0)
+
+extern int external_int;
+extern int * global_upi;
+extern int * IQ global_iqpi;
+extern int * AQ global_aqpi;
+extern void use_upi(int *ptr);
+
+typedef void func_t(void);
+extern void external_func(void);
+extern func_t *global_upf;
+extern func_t * IQ global_iqpf;
+extern func_t * AQ global_aqpf;
+extern void use_upf(func_t *ptr);
+
+// Data with address-independent qualifiers.
+
+// CHECK-LABEL: define void @test_store_data_i_constant()
+void test_store_data_i_constant() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @external_int to i64), i32 1, i64 50)
+// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * IQ iqpi = &external_int;
+// CHECK-NEXT: [[T0:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @external_int to i64), i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T0]] to ptr
+// CHECK-NEXT: store ptr [[SIGNED]], ptr [[V]],
+// CHECK-NEXT: ret void
+ iqpi = &external_int;
+}
+
+// CHECK-LABEL: define void @test_store_data_iu()
+void test_store_data_iu() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[T0]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * IQ iqpi = global_upi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[T0]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ iqpi = global_upi;
+}
+
+// CHECK-LABEL: define void @test_store_data_ia()
+void test_store_data_ia() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * IQ iqpi = global_aqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ iqpi = global_aqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[RESULT:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[RESULT]], ptr [[V]],
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[RESULT]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[RESULT]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 1, i64 50)
+// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[RESULT:%.*]] = phi ptr [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ]
+// CHECK-NEXT: call void @use_upi(ptr noundef [[RESULT]])
+ use_upi(iqpi = global_aqpi);
+}
+
+// CHECK-LABEL: define void @test_store_data_ii_same()
+void test_store_data_ii_same() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: store ptr [[LOAD]], ptr [[V]],
+ int * IQ iqpi = global_iqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: store ptr [[LOAD]], ptr [[V]],
+ iqpi = global_iqpi;
+}
+
+// CHECK-LABEL: define void @test_store_data_ii_different()
+void test_store_data_ii_different() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 100)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * DIFF_IQ iqpi = global_iqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 100)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ iqpi = global_iqpi;
+}
+
+// CHECK-LABEL: define void @test_store_data_ii_zero()
+void test_store_data_ii_zero() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 0)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * ZERO_IQ iqpi = global_iqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr [[V]]
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 0, i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr @global_iqpi,
+ global_iqpi = iqpi;
+}
+
+// CHECK-LABEL: define void @test_load_data_i()
+void test_load_data_i() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 1, i64 50)
+// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int *upi = global_iqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 1, i64 50)
+// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ upi = global_iqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 1, i64 50)
+// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ]
+// CHECK-NEXT: call void @use_upi(ptr noundef [[T0]])
+ use_upi(global_iqpi);
+}
+
+// Data with address-discriminated qualifiers.
+
+// CHECK-LABEL: define void @test_store_data_a_constant()
+void test_store_data_a_constant() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @external_int to i64), i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * AQ aqpi = &external_int;
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @external_int to i64), i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpi = &external_int;
+}
+
+// CHECK-LABEL: define void @test_store_data_au()
+void test_store_data_au() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upi,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[T0]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * AQ aqpi = global_upi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upi,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[T0]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpi = global_upi;
+}
+
+// CHECK-LABEL: define void @test_store_data_ai()
+void test_store_data_ai() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * AQ aqpi = global_iqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpi,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpi = global_iqpi;
+}
+
+// CHECK-LABEL: define void @test_store_data_aa_same()
+void test_store_data_aa_same() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * AQ aqpi = global_aqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpi = global_aqpi;
+}
+
+// CHECK-LABEL: define void @test_store_data_aa_different()
+void test_store_data_aa_different() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 100)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * DIFF_AQ aqpi = global_aqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 100)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpi = global_aqpi;
+}
+
+// CHECK-LABEL: define void @test_store_data_aa_zero()
+void test_store_data_aa_zero() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[NEWDISC:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int * ZERO_AQ aqpi = global_aqpi;
+// CHECK: [[LOAD:%.*]] = load ptr, ptr [[V]],
+// CHECK-NEXT: [[OLDDISC:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr @global_aqpi,
+ global_aqpi = aqpi;
+}
+
+// CHECK-LABEL: define void @test_load_data_a()
+void test_load_data_a() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 1, i64 [[OLDDISC]])
+// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ int *upi = global_aqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 1, i64 [[OLDDISC]])
+// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ upi = global_aqpi;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpi,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpi to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T0]], i32 1, i64 [[OLDDISC]])
+// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ]
+// CHECK-NEXT: call void @use_upi(ptr noundef [[T0]])
+ use_upi(global_aqpi);
+}
+
+// Function with address-independent qualifiers.
+
+// CHECK-LABEL: define void @test_store_function_i_constant()
+void test_store_function_i_constant() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 50)
+// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * IQ iqpf = &external_func;
+// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 50)
+// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ iqpf = &external_func;
+}
+
+// CHECK-LABEL: define void @test_store_function_iu()
+void test_store_function_iu() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upf,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * IQ iqpf = global_upf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upf,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ iqpf = global_upf;
+}
+
+// CHECK-LABEL: define void @test_store_function_ia()
+void test_store_function_ia() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * IQ iqpf = global_aqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ iqpf = global_aqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[RESULT:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[RESULT]], ptr [[V]],
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[RESULT]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[RESULT]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: call void @use_upf(ptr noundef [[T0]])
+ use_upf(iqpf = global_aqpf);
+}
+
+// CHECK-LABEL: define void @test_store_function_ii_same()
+void test_store_function_ii_same() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: store ptr [[LOAD]], ptr [[V]],
+ func_t * IQ iqpf = global_iqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: store ptr [[LOAD]], ptr [[V]],
+ iqpf = global_iqpf;
+}
+
+// CHECK-LABEL: define void @test_store_function_ii_different()
+void test_store_function_ii_different() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 100)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * DIFF_IQ iqpf = global_iqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 100)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ iqpf = global_iqpf;
+}
+
+// CHECK-LABEL: define void @test_load_function_i()
+void test_load_function_i() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t *upf = global_iqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ upf = global_iqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: call void @use_upf(ptr noundef [[T0]])
+ use_upf(global_iqpf);
+}
+
+// Function with address-discriminated qualifiers.
+
+// CHECK-LABEL: define void @test_store_function_a_constant()
+void test_store_function_a_constant() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * AQ aqpf = &external_func;
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpf = &external_func;
+}
+
+// CHECK-LABEL: define void @test_store_function_au()
+void test_store_function_au() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upf,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * AQ aqpf = global_upf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_upf,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpf = global_upf;
+}
+
+// CHECK-LABEL: define void @test_store_function_ai()
+void test_store_function_ai() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * AQ aqpf = global_iqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_iqpf,
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpf = global_iqpf;
+}
+
+// CHECK-LABEL: define void @test_store_function_aa_same()
+void test_store_function_aa_same() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * AQ aqpf = global_aqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpf = global_aqpf;
+}
+
+// CHECK-LABEL: define void @test_store_function_aa_different()
+void test_store_function_aa_different() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 100)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t * DIFF_AQ aqpf = global_aqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64
+// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 100)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ aqpf = global_aqpf;
+}
+
+// CHECK-LABEL: define void @test_load_function_a()
+void test_load_function_a() {
+// CHECK: [[V:%.*]] = alloca ptr,
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 18983)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ func_t *upf = global_aqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 18983)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: store ptr [[T0]], ptr [[V]],
+ upf = global_aqpf;
+// CHECK-NEXT: [[LOAD:%.*]] = load ptr, ptr @global_aqpf,
+// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @global_aqpf to i64), i64 50)
+// CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK-NEXT: br i1 [[T0]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 18983)
+// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK-NEXT: br label
+// CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ]
+// CHECK-NEXT: call void @use_upf(ptr noundef [[T0]])
+ use_upf(global_aqpf);
+}
diff --git a/clang/test/CodeGen/ptrauth-qualifier.c b/clang/test/CodeGen/ptrauth-qualifier.c
new file mode 100644
index 0000000000000..5a664d296ce0e
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-qualifier.c
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s
+
+#include <ptrauth.h>
+
+// Constant initializers for data pointers.
+extern int external_int;
+
+// CHECK: @g1 = global ptr ptrauth (ptr @external_int, i32 1, i64 56)
+int * __ptrauth(1,0,56) g1 = &external_int;
+
+// CHECK: @g2 = global ptr ptrauth (ptr @external_int, i32 1, i64 1272, ptr @g2)
+int * __ptrauth(1,1,1272) g2 = &external_int;
+
+// CHECK: @g3 = global ptr null
+int * __ptrauth(1,1,871) g3 = 0;
+
+// FIXME: should we make a ptrauth constant for this absolute symbol?
+// CHECK: @g4 = global ptr inttoptr (i64 1230 to ptr)
+int * __ptrauth(1,1,1902) g4 = (int*) 1230;
+
+// CHECK: @ga = global [3 x ptr] [
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 712, ptr @ga),
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 1)),
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 2))]
+int * __ptrauth(1,1,712) ga[3] = { &external_int, &external_int, &external_int };
+
+struct A {
+ int * __ptrauth(1,0,431) f0;
+ int * __ptrauth(1,0,9182) f1;
+ int * __ptrauth(1,0,783) f2;
+};
+
+// CHECK: @gs1 = global %struct.A {
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 431),
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 9182),
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 783) }
+struct A gs1 = { &external_int, &external_int, &external_int };
+
+struct B {
+ int * __ptrauth(1,1,1276) f0;
+ int * __ptrauth(1,1,23674) f1;
+ int * __ptrauth(1,1,163) f2;
+};
+
+// CHECK: @gs2 = global %struct.B {
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 1276, ptr @gs2),
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 23674, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 1)),
+// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 163, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2)) }
+struct B gs2 = { &external_int, &external_int, &external_int };
+
+// Constant initializers for function pointers.
+extern void external_function(void);
+typedef void (*fpt)(void);
+
+// CHECK: @f1 = global ptr ptrauth (ptr @external_function, i32 1, i64 56)
+fpt __ptrauth(1,0,56) f1 = &external_function;
+
+// CHECK: @f2 = global ptr ptrauth (ptr @external_function, i32 1, i64 1272, ptr @f2)
+fpt __ptrauth(1,1,1272) f2 = &external_function;
+
+// CHECK: @fa = global [3 x ptr] [
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 712, ptr @fa),
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 1)),
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 2))]
+fpt __ptrauth(1,1,712) fa[3] = { &external_function, &external_function, &external_function };
+
+struct C {
+ fpt __ptrauth(1,0,431) f0;
+ fpt __ptrauth(1,0,9182) f1;
+ fpt __ptrauth(1,0,783) f2;
+};
+// CHECK: @fs1 = global %struct.C {
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 431),
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 9182),
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 783) }
+struct C fs1 = { &external_function, &external_function, &external_function };
+
+struct D {
+ fpt __ptrauth(1,1,1276) f0;
+ fpt __ptrauth(1,1,23674) f1;
+ fpt __ptrauth(1,1,163) f2;
+};
+// CHECK: @fs2 = global %struct.D {
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 1276, ptr @fs2),
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 23674, ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 1)),
+// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 163, ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 2)) }
+struct D fs2 = { &external_function, &external_function, &external_function };
diff --git a/clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp b/clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp
new file mode 100644
index 0000000000000..d38289cd7cef7
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp
@@ -0,0 +1,167 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -std=c++11 -emit-llvm %s -o - | FileCheck %s
+
+#define AQ __ptrauth(1,1,50)
+#define IQ __ptrauth(1,0,50)
+
+// CHECK: %[[STRUCT_SA:.*]] = type { ptr, ptr }
+// CHECK: %[[STRUCT_SI:.*]] = type { ptr }
+
+struct SA {
+ int * AQ m0; // Signed using address discrimination.
+ int * AQ m1; // Signed using address discrimination.
+};
+
+struct SI {
+ int * IQ m; // No address discrimination.
+};
+
+struct __attribute__((trivial_abi)) TrivialSA {
+ int * AQ m0; // Signed using address discrimination.
+ int * AQ m1; // Signed using address discrimination.
+};
+
+// Check that TrivialSA is passed indirectly despite being annotated with
+// 'trivial_abi'.
+
+// CHECK: define void @_Z18testParamTrivialSA9TrivialSA(ptr noundef %{{.*}})
+
+void testParamTrivialSA(TrivialSA a) {
+}
+
+// CHECK: define void @_Z19testCopyConstructor2SA(ptr
+// CHECK: call noundef ptr @_ZN2SAC1ERKS_(
+
+// CHECK: define linkonce_odr noundef ptr @_ZN2SAC1ERKS_(
+// CHECK: call noundef ptr @_ZN2SAC2ERKS_(
+
+void testCopyConstructor(SA a) {
+ SA t = a;
+}
+
+// CHECK: define void @_Z19testMoveConstructor2SA(ptr
+// CHECK: call noundef ptr @_ZN2SAC1EOS_(
+
+// CHECK: define linkonce_odr noundef ptr @_ZN2SAC1EOS_(
+// CHECK: call noundef ptr @_ZN2SAC2EOS_(
+
+void testMoveConstructor(SA a) {
+ SA t = static_cast<SA &&>(a);
+}
+
+// CHECK: define void @_Z18testCopyAssignment2SA(ptr
+// CHECK: call noundef nonnull align 8 dereferenceable(16) ptr @_ZN2SAaSERKS_(
+
+// CHECK: define linkonce_odr noundef nonnull align 8 dereferenceable(16) ptr @_ZN2SAaSERKS_(ptr noundef nonnull align 8 dereferenceable(16) %[[THIS:.*]], ptr noundef nonnull align 8 dereferenceable(16) %0)
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8
+// CHECK: %[[_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8
+// CHECK: store ptr %[[V0:.*]], ptr %[[_ADDR]], align 8
+// CHECK: %[[THISI:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8
+// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[THISI]], i32 0, i32 0
+// CHECK: %[[V1:.*]] = load ptr, ptr %[[_ADDR]], align 8
+// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[V1]], i32 0, i32 0
+// CHECK: %[[V2:.*]] = load ptr, ptr %[[M02]], align 8
+// CHECK: %[[V3:.*]] = ptrtoint ptr %[[M02]] to i64
+// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V3]], i64 50)
+// CHECK: %[[V5:.*]] = ptrtoint ptr %[[M0]] to i64
+// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V5]], i64 50)
+// CHECK: %[[V8:.*]] = ptrtoint ptr %[[V2]] to i64
+// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]])
+
+void testCopyAssignment(SA a) {
+ SA t;
+ t = a;
+}
+
+// CHECK: define void @_Z18testMoveAssignment2SA(ptr
+// CHECK: call noundef nonnull align 8 dereferenceable(16) ptr @_ZN2SAaSEOS_(
+
+// CHECK: define linkonce_odr noundef nonnull align 8 dereferenceable(16) ptr @_ZN2SAaSEOS_(ptr noundef nonnull align 8 dereferenceable(16) %[[THIS:.*]], ptr noundef nonnull align 8 dereferenceable(16) %0)
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8
+// CHECK: %[[_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8
+// CHECK: store ptr %[[V0:.*]], ptr %[[_ADDR]], align 8
+// CHECK: %[[THISI:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8
+// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[THISI]], i32 0, i32 0
+// CHECK: %[[V1:.*]] = load ptr, ptr %[[_ADDR]], align 8
+// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[V1]], i32 0, i32 0
+// CHECK: %[[V2:.*]] = load ptr, ptr %[[M02]], align 8
+// CHECK: %[[V3:.*]] = ptrtoint ptr %[[M02]] to i64
+// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V3]], i64 50)
+// CHECK: %[[V5:.*]] = ptrtoint ptr %[[M0]] to i64
+// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V5]], i64 50)
+// CHECK: %[[V8:.*]] = ptrtoint ptr %[[V2]] to i64
+// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]])
+
+void testMoveAssignment(SA a) {
+ SA t;
+ t = static_cast<SA &&>(a);
+}
+
+// CHECK: define void @_Z19testCopyConstructor2SI(i
+// CHECK: call void @llvm.memcpy.p0.p0.i64(
+
+void testCopyConstructor(SI a) {
+ SI t = a;
+}
+
+// CHECK: define void @_Z19testMoveConstructor2SI(
+// CHECK: call void @llvm.memcpy.p0.p0.i64(
+
+void testMoveConstructor(SI a) {
+ SI t = static_cast<SI &&>(a);
+}
+
+// CHECK: define void @_Z18testCopyAssignment2SI(
+// CHECK: call void @llvm.memcpy.p0.p0.i64(
+
+void testCopyAssignment(SI a) {
+ SI t;
+ t = a;
+}
+
+// CHECK: define void @_Z18testMoveAssignment2SI(
+// CHECK: call void @llvm.memcpy.p0.p0.i64(
+
+void testMoveAssignment(SI a) {
+ SI t;
+ t = static_cast<SI &&>(a);
+}
+
+// CHECK: define linkonce_odr noundef ptr @_ZN2SAC2ERKS_(ptr noundef nonnull align 8 dereferenceable(16) %[[THIS:.*]], ptr noundef nonnull align 8 dereferenceable(16) %0)
+// CHECK: %[[RETVAL:.*]] = alloca ptr, align 8
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8
+// CHECK: %[[_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8
+// CHECK: store ptr %[[V0:.*]], ptr %[[_ADDR]], align 8
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8
+// CHECK: store ptr %[[THIS1]], ptr %[[RETVAL]], align 8
+// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[THIS1]], i32 0, i32 0
+// CHECK: %[[V1:.*]] = load ptr, ptr %[[_ADDR]], align 8
+// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[V1]], i32 0, i32 0
+// CHECK: %[[V2:.*]] = load ptr, ptr %[[M02]], align 8
+// CHECK: %[[V3:.*]] = ptrtoint ptr %[[M02]] to i64
+// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V3]], i64 50)
+// CHECK: %[[V5:.*]] = ptrtoint ptr %[[M0]] to i64
+// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V5]], i64 50)
+// CHECK: %[[V8:.*]] = ptrtoint ptr %[[V2]] to i64
+// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]])
+
+// CHECK: define linkonce_odr noundef ptr @_ZN2SAC2EOS_(ptr noundef nonnull align 8 dereferenceable(16) %[[THIS:.*]], ptr noundef nonnull align 8 dereferenceable(16) %0)
+// CHECK: %[[RETVAL:.*]] = alloca ptr, align 8
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8
+// CHECK: %[[_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8
+// CHECK: store ptr %[[V0:.*]], ptr %[[_ADDR]], align 8
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8
+// CHECK: store ptr %[[THIS1]], ptr %[[RETVAL]], align 8
+// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[THIS1]], i32 0, i32 0
+// CHECK: %[[V1:.*]] = load ptr, ptr %[[_ADDR]], align 8
+// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %[[V1]], i32 0, i32 0
+// CHECK: %[[V2:.*]] = load ptr, ptr %[[M02]], align 8
+// CHECK: %[[V3:.*]] = ptrtoint ptr %[[M02]] to i64
+// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V3]], i64 50)
+// CHECK: %[[V5:.*]] = ptrtoint ptr %[[M0]] to i64
+// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V5]], i64 50)
+// CHECK: %[[V8:.*]] = ptrtoint ptr %[[V2]] to i64
+// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]])
diff --git a/clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm b/clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm
new file mode 100644
index 0000000000000..e5cb71bad47c0
--- /dev/null
+++ b/clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios11 -fptrauth-calls -fptrauth-intrinsics -std=c++11 -fobjc-arc -emit-llvm -o - %s | FileCheck %s
+
+#define AQ __ptrauth(1,1,50)
+
+struct AddrDiscStrong0 {
+ int * AQ f0; // Signed using address discrimination.
+ __strong id f1;
+};
+
+struct AddrDiscStrong1 {
+ AddrDiscStrong1(const AddrDiscStrong1 &);
+ int * AQ f0; // Signed using address discrimination.
+ __strong id f1;
+};
+
+// Check that AddrDiscStrong0 is destructed in the callee.
+
+// CHECK: define void @_Z24testParamAddrDiscStrong015AddrDiscStrong0(ptr noundef %[[A:.*]])
+// CHECK: call noundef ptr @_ZN15AddrDiscStrong0D1Ev(ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %[[A]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr noundef ptr @_ZN15AddrDiscStrong0D1Ev(
+
+void testParamAddrDiscStrong0(AddrDiscStrong0 a) {
+}
+
+// Check that AddrDiscStrong1 is not destructed in the callee because it has a
+// non-trivial copy constructor.
+
+// CHECK: define void @_Z24testParamAddrDiscStrong115AddrDiscStrong1(ptr noundef %{{.*}})
+// CHECK-NOT: call
+// CHECK: ret void
+
+void testParamAddrDiscStrong1(AddrDiscStrong1 a) {
+}
diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c
index 14059f827b94c..14ef80860d0e9 100644
--- a/clang/test/Preprocessor/ptrauth_feature.c
+++ b/clang/test/Preprocessor/ptrauth_feature.c
@@ -85,6 +85,8 @@ void has_ptrauth_type_info_vtable_pointer_discrimination() {}
void no_ptrauth_type_info_vtable_pointer_discrimination() {}
#endif
+#include <ptrauth.h>
+
#if __has_feature(ptrauth_function_pointer_type_discrimination)
// FUNC: has_ptrauth_function_pointer_type_discrimination
void has_ptrauth_function_pointer_type_discrimination() {}
@@ -108,3 +110,11 @@ void has_ptrauth_indirect_gotos() {}
// NOGOTOS: no_ptrauth_indirect_gotos
void no_ptrauth_indirect_gotos() {}
#endif
+
+#if __has_feature(ptrauth_qualifier)
+// INTRIN: has_ptrauth_qualifier
+void has_ptrauth_qualifier() {}
+#else
+// NOINTRIN: no_ptrauth_qualifier
+void no_ptrauth_qualifier() {}
+#endif
diff --git a/clang/test/Sema/atomic-ops-ptrauth.c b/clang/test/Sema/atomic-ops-ptrauth.c
new file mode 100644
index 0000000000000..65b58379c8552
--- /dev/null
+++ b/clang/test/Sema/atomic-ops-ptrauth.c
@@ -0,0 +1,117 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s
+
+#include <stdatomic.h>
+
+int i;
+int *__ptrauth(2, 1, 100) authenticated_ptr = &i;
+int *__ptrauth(2, 0, 200) non_addr_discriminatedauthenticated_ptr = &i;
+int * wat = &i;
+#define ATOMIZE(p) (__typeof__(p) volatile _Atomic *)(long)(&p)
+
+void f() {
+ static int j = 1;
+ __c11_atomic_init(ATOMIZE(authenticated_ptr), 5);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __c11_atomic_store(ATOMIZE(authenticated_ptr), 0, memory_order_relaxed);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __c11_atomic_load(ATOMIZE(authenticated_ptr), memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __c11_atomic_store(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_store_n(ATOMIZE(authenticated_ptr), 4, memory_order_release);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_store(ATOMIZE(authenticated_ptr), j, memory_order_release);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __c11_atomic_exchange(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_exchange(ATOMIZE(authenticated_ptr), &j, &j, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __c11_atomic_fetch_add(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_fetch_add(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_fetch_sub(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_fetch_min(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_fetch_max(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __c11_atomic_fetch_and(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_fetch_and(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_fetch_or(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+ __atomic_fetch_xor(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst);
+ // expected-error at -1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}}
+
+ __c11_atomic_init(ATOMIZE(non_addr_discriminatedauthenticated_ptr), &j);
+ __c11_atomic_store(ATOMIZE(non_addr_discriminatedauthenticated_ptr), 0, memory_order_relaxed);
+ __c11_atomic_load(ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_seq_cst);
+ __atomic_store(&j, ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_release);
+ // expected-warning at -1 {{incompatible pointer types passing 'volatile __ptrauth(2,0,200) _Atomic(int *) *' to parameter of type 'int *'}}
+ __c11_atomic_exchange(ATOMIZE(j), ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_seq_cst);
+ // expected-error at -1 {{incompatible pointer to integer conversion passing 'volatile __ptrauth(2,0,200) _Atomic(int *) *' to parameter of type 'typeof (j)' (aka 'int')}}
+ __c11_atomic_fetch_add(ATOMIZE(non_addr_discriminatedauthenticated_ptr), ATOMIZE(j), memory_order_seq_cst);
+ // expected-error at -1 {{incompatible pointer to integer conversion passing 'volatile _Atomic(typeof (j)) *' to parameter of type 'long'}}
+ __c11_atomic_fetch_and(ATOMIZE(j), ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_seq_cst);
+ // expected-error at -1 {{incompatible pointer to integer conversion passing 'volatile __ptrauth(2,0,200) _Atomic(int *) *' to parameter of type 'typeof (j)' (aka 'int')}}
+
+
+ __sync_fetch_and_add(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_fetch_and_sub(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_fetch_and_or(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_fetch_and_and(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_fetch_and_xor(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_fetch_and_nand(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+
+ __sync_add_and_fetch(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_sub_and_fetch(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_or_and_fetch(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_and_and_fetch(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_xor_and_fetch(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_nand_and_fetch(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+
+ __sync_bool_compare_and_swap(&authenticated_ptr, 1, 0);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_val_compare_and_swap(&authenticated_ptr, 1, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+
+ __sync_lock_test_and_set(&authenticated_ptr, 1);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+ __sync_lock_release(&authenticated_ptr);
+ // expected-error at -1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}}
+
+
+int i = 0;
+
+ __sync_fetch_and_add(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_fetch_and_sub(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_fetch_and_or(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_fetch_and_and(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_fetch_and_xor(&non_addr_discriminatedauthenticated_ptr, &i);
+
+ __sync_add_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_sub_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_or_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_and_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_xor_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i);
+
+ __sync_bool_compare_and_swap(&non_addr_discriminatedauthenticated_ptr, &i, &i);
+ __sync_val_compare_and_swap(&non_addr_discriminatedauthenticated_ptr, &i, &i);
+
+ __sync_lock_test_and_set(&non_addr_discriminatedauthenticated_ptr, &i);
+ __sync_lock_release(&non_addr_discriminatedauthenticated_ptr);
+}
diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c
new file mode 100644
index 0000000000000..02e511a08ddeb
--- /dev/null
+++ b/clang/test/Sema/ptrauth-qualifier.c
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s
+
+#include <ptrauth.h>
+
+#if __has_feature(ptrauth_qualifier)
+#warning __ptrauth qualifier enabled!
+// expected-warning at -1 {{__ptrauth qualifier enabled!}}
+#endif
+
+#if __aarch64__
+#define VALID_CODE_KEY 0
+#define VALID_DATA_KEY 2
+#define INVALID_KEY 200
+#else
+#error Provide these constants if you port this test
+#endif
+
+int * __ptrauth(VALID_DATA_KEY) valid0;
+
+typedef int *intp;
+
+int nonConstantGlobal = 5;
+
+__ptrauth int invalid0; // expected-error{{expected '('}}
+__ptrauth() int invalid1; // expected-error{{expected expression}}
+__ptrauth(INVALID_KEY) int invalid2; // expected-error{{200 does not identify a valid pointer authentication key for the current target}}
+__ptrauth(VALID_DATA_KEY) int invalid3; // expected-error{{__ptrauth qualifier may only be applied to pointer types}}
+__ptrauth(VALID_DATA_KEY) int *invalid4; // expected-error{{__ptrauth qualifier may only be applied to pointer types}}
+int * (__ptrauth(VALID_DATA_KEY) invalid5); // expected-error{{expected identifier or '('}} expected-error{{expected ')'}} expected-note {{to match this '('}}
+int *__ptrauth(VALID_DATA_KEY) __ptrauth(VALID_DATA_KEY) invalid6; // expected-error{{type 'int *__ptrauth(2,0,0)' is already __ptrauth-qualified}}
+int * __ptrauth(VALID_DATA_KEY, 2) invalid7; // expected-error{{address discrimination flag for __ptrauth must be 0 or 1; value is 2}}
+int * __ptrauth(VALID_DATA_KEY, -1) invalid8; // expected-error{{address discrimination flag for __ptrauth must be 0 or 1; value is -1}}
+int * __ptrauth(VALID_DATA_KEY, 1, -1) invalid9; // expected-error{{extra discriminator for __ptrauth must be between 0 and 65535; value is -1}}
+int * __ptrauth(VALID_DATA_KEY, 1, 100000) invalid10; // expected-error{{extra discriminator for __ptrauth must be between 0 and 65535; value is 100000}}
+int * __ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // expected-error{{argument to __ptrauth must be an integer constant expression}}
+
+int * __ptrauth(VALID_DATA_KEY) valid0;
+int * __ptrauth(VALID_DATA_KEY) *valid1;
+__ptrauth(VALID_DATA_KEY) intp valid2;
+__ptrauth(VALID_DATA_KEY) intp *valid3;
+intp __ptrauth(VALID_DATA_KEY) valid4;
+intp __ptrauth(VALID_DATA_KEY) *valid5;
+int * __ptrauth(VALID_DATA_KEY, 0) valid6;
+int * __ptrauth(VALID_DATA_KEY, 1) valid7;
+int * __ptrauth(VALID_DATA_KEY, (_Bool) 1) valid8;
+int * __ptrauth(VALID_DATA_KEY, 1, 0) valid9;
+int * __ptrauth(VALID_DATA_KEY, 1, 65535) valid10;
+
+extern intp redeclaration0; // expected-note {{previous declaration}}
+extern intp __ptrauth(VALID_DATA_KEY) redeclaration0; // expected-error{{redeclaration of 'redeclaration0' with a different type: '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)') vs 'intp' (aka 'int *')}}
+
+extern intp redeclaration1; // expected-note {{previous declaration}}
+extern intp __ptrauth(VALID_DATA_KEY) redeclaration1; // expected-error{{redeclaration of 'redeclaration1' with a different type: '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)') vs 'intp' (aka 'int *')}}
+
+intp __ptrauth(VALID_DATA_KEY) redeclaration2; // expected-note {{previous definition}}
+intp redeclaration2 = 0; // expected-error{{redefinition of 'redeclaration2' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}}
+
+intp __ptrauth(VALID_DATA_KEY) redeclaration3; // expected-note {{previous definition}}
+intp redeclaration3 = 0; // expected-error{{redefinition of 'redeclaration3' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}}
+
+void illegal0(intp __ptrauth(VALID_DATA_KEY)); // expected-error{{parameter types may not be qualified with __ptrauth}}
+intp __ptrauth(VALID_DATA_KEY) illegal1(void); // expected-error{{return types may not be qualified with __ptrauth}}
+
+void test_code(intp p) {
+ p = (intp __ptrauth(VALID_DATA_KEY)) 0; // expected-error{{cast types may not be qualified with __ptrauth}}
+
+ __ptrauth(VALID_DATA_KEY) intp pSpecial = p;
+ pSpecial = p;
+ intp pNormal = pSpecial;
+ pNormal = pSpecial;
+
+ intp __ptrauth(VALID_DATA_KEY) *ppSpecial0 = &pSpecial;
+ intp __ptrauth(VALID_DATA_KEY) *ppSpecial1 = &pNormal; // expected-error {{initializing '__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') with an expression of type 'intp *' (aka 'int **') changes pointer-authentication of pointee type}}
+ intp *ppNormal0 = &pSpecial; // expected-error {{initializing 'intp *' (aka 'int **') with an expression of type '__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') changes pointer-authentication of pointee type}}
+ intp *ppNormal1 = &pNormal;
+
+ intp *pp5 = (p ? &pSpecial : &pNormal); // expected-error {{__ptrauth qualification mismatch ('__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') and 'intp *' (aka 'int **'))}}
+}
+
+void test_array(void) {
+ intp __ptrauth(VALID_DATA_KEY) pSpecialArray[10];
+ intp __ptrauth(VALID_DATA_KEY) *ppSpecial0 = pSpecialArray;
+ intp __ptrauth(VALID_DATA_KEY) *ppSpecial1 = &pSpecialArray[0];
+}
diff --git a/clang/test/SemaCXX/ptrauth-qualifier.cpp b/clang/test/SemaCXX/ptrauth-qualifier.cpp
new file mode 100644
index 0000000000000..2cd9ebe356f61
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-qualifier.cpp
@@ -0,0 +1,141 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++11 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s
+
+#define AQ __ptrauth(1,1,50)
+#define AQ2 __ptrauth(1,1,51)
+#define IQ __ptrauth(1,0,50)
+
+struct __attribute__((trivial_abi)) AddrDisc { // expected-warning {{'trivial_abi' cannot be applied to 'AddrDisc'}} expected-note {{'trivial_abi' is disallowed on 'AddrDisc' because it has an address-discriminated __ptrauth field}}
+ int * AQ m0;
+};
+
+struct __attribute__((trivial_abi)) NoAddrDisc {
+ int * IQ m0;
+};
+
+namespace test_union {
+
+ union U0 {
+ int * AQ f0; // expected-note 4 {{'U0' is implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}}
+
+ // ptrauth fields that don't have an address-discriminated qualifier don't
+ // delete the special functions.
+ int * IQ f1;
+ };
+
+ union U1 {
+ int * AQ f0; // expected-note 8 {{'U1' is implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}}
+ U1() = default;
+ ~U1() = default;
+ U1(const U1 &) = default; // expected-warning {{explicitly defaulted copy constructor is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}} expected-note{{replace 'default'}}
+ U1(U1 &&) = default; // expected-warning {{explicitly defaulted move constructor is implicitly deleted}} expected-note{{replace 'default'}}
+ U1 & operator=(const U1 &) = default; // expected-warning {{explicitly defaulted copy assignment operator is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}} expected-note{{replace 'default'}}
+ U1 & operator=(U1 &&) = default; // expected-warning {{explicitly defaulted move assignment operator is implicitly deleted}} expected-note{{replace 'default'}}
+ };
+
+ // It's fine if the user has explicitly defined the special functions.
+ union U2 {
+ int * AQ f0;
+ U2() = default;
+ ~U2() = default;
+ U2(const U2 &);
+ U2(U2 &&);
+ U2 & operator=(const U2 &);
+ U2 & operator=(U2 &&);
+ };
+
+ // Address-discriminated ptrauth fields in anonymous union fields delete the
+ // defaulted copy/move constructors/assignment operators of the containing
+ // class.
+ struct S0 {
+ union {
+ int * AQ f0; // expected-note 4 {{' is implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}}
+ char f1;
+ };
+ };
+
+ struct S1 {
+ union {
+ union {
+ int * AQ f0; // expected-note 4 {{implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}}
+ char f1;
+ } u; // expected-note 4 {{'S1' is implicitly deleted because field 'u' has a deleted}}
+ int f2;
+ };
+ };
+
+ U0 *x0;
+ U1 *x1;
+ U2 *x2;
+ S0 *x3;
+ S1 *x4;
+
+ // No diagnostics since constructors/destructors of the unions aren't deleted by default.
+ void testDefaultConstructor() {
+ U0 u0;
+ U1 u1;
+ U2 u2;
+ S0 s0;
+ S1 s1;
+ }
+
+ // No diagnostics since destructors of the unions aren't deleted by default.
+ void testDestructor(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) {
+ delete u0;
+ delete u1;
+ delete u2;
+ delete s0;
+ delete s1;
+ }
+
+ void testCopyConstructor(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) {
+ U0 t0(*u0); // expected-error {{call to implicitly-deleted copy constructor}}
+ U1 t1(*u1); // expected-error {{call to implicitly-deleted copy constructor}}
+ U2 t2(*u2);
+ S0 t3(*s0); // expected-error {{call to implicitly-deleted copy constructor}}
+ S1 t4(*s1); // expected-error {{call to implicitly-deleted copy constructor}}
+ }
+
+ void testCopyAssignment(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) {
+ *x0 = *u0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ *x1 = *u1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ *x2 = *u2;
+ *x3 = *s0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ *x4 = *s1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ }
+
+ void testMoveConstructor(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) {
+ U0 t0(static_cast<U0 &&>(*u0)); // expected-error {{call to implicitly-deleted copy constructor}}
+ U1 t1(static_cast<U1 &&>(*u1)); // expected-error {{call to implicitly-deleted copy constructor}}
+ U2 t2(static_cast<U2 &&>(*u2));
+ S0 t3(static_cast<S0 &&>(*s0)); // expected-error {{call to implicitly-deleted copy constructor}}
+ S1 t4(static_cast<S1 &&>(*s1)); // expected-error {{call to implicitly-deleted copy constructor}}
+ }
+
+ void testMoveAssignment(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) {
+ *x0 = static_cast<U0 &&>(*u0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ *x1 = static_cast<U1 &&>(*u1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ *x2 = static_cast<U2 &&>(*u2);
+ *x3 = static_cast<S0 &&>(*s0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ *x4 = static_cast<S1 &&>(*s1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}}
+ }
+}
+
+bool test_composite_type0(bool c, int * AQ * a0, int * AQ * a1) {
+ auto t = c ? a0 : a1;
+ return a0 == a1;
+}
+
+bool test_composite_type1(bool c, int * AQ * a0, int * AQ2 * a1) {
+ auto t = c ? a0 : a1; // expected-error {{incompatible operand types ('int *__ptrauth(1,1,50) *' and 'int *__ptrauth(1,1,51) *')}}
+ return a0 == a1; // expected-error {{comparison of distinct pointer types ('int *__ptrauth(1,1,50) *' and 'int *__ptrauth(1,1,51) *')}}
+}
+
+void test_bad_call_diag(void *AQ *ptr); // expected-note{{candidate function not viable: 1st argument ('void *__ptrauth(1,1,51) *') has __ptrauth(1,1,51) qualifier, but parameter has __ptrauth(1,1,50) qualifier}} expected-note{{candidate function not viable: 1st argument ('void **') has no ptrauth qualifier, but parameter has __ptrauth(1,1,50) qualifier}}
+void test_bad_call_diag2(void **ptr); // expected-note{{1st argument ('void *__ptrauth(1,1,50) *') has __ptrauth(1,1,50) qualifier, but parameter has no ptrauth qualifier}}
+
+int test_call_diag() {
+ void *AQ ptr1, *AQ2 ptr2, *ptr3;
+ test_bad_call_diag(&ptr2); // expected-error {{no matching function for call to 'test_bad_call_diag'}}
+ test_bad_call_diag(&ptr3); // expected-error {{no matching function for call to 'test_bad_call_diag'}}
+ test_bad_call_diag2(&ptr1); // expected-error {{no matching function for call to 'test_bad_call_diag2'}}
+}
diff --git a/clang/test/SemaCXX/ptrauth-template-parameters.cpp b/clang/test/SemaCXX/ptrauth-template-parameters.cpp
new file mode 100644
index 0000000000000..2b09b70046d53
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-template-parameters.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple arm64e-apple-ios -fsyntax-only -verify -fptrauth-intrinsics -std=c++20 %s
+
+template <typename T> struct G {
+ T __ptrauth(0,0,1234) test;
+ // expected-error at -1 2 {{type '__ptrauth(0,0,1234) T' is already __ptrauth-qualified}}
+};
+
+template <typename T> struct Indirect {
+ G<T> layers;
+ // expected-note at -1{{in instantiation of template class 'G<void *__ptrauth(0,0,1235)>' requested here}}
+ // expected-note at -2{{in instantiation of template class 'G<void *__ptrauth(0,0,1234)>' requested here}}
+};
+
+void f3() {
+ Indirect<void* __ptrauth(0,0,1234)> one;
+ // expected-note at -1{{in instantiation of template class 'Indirect<void *__ptrauth(0,0,1234)>' requested here}}
+ Indirect<void* __ptrauth(0,0,1235)> two;
+ // expected-note at -1{{in instantiation of template class 'Indirect<void *__ptrauth(0,0,1235)>' requested here}}
+ Indirect<void*> three;
+}
diff --git a/clang/test/SemaObjC/ptrauth-qualifier.m b/clang/test/SemaObjC/ptrauth-qualifier.m
new file mode 100644
index 0000000000000..e5d424eaf92fd
--- /dev/null
+++ b/clang/test/SemaObjC/ptrauth-qualifier.m
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s
+
+#if __has_feature(ptrauth_qualifier)
+#warning __ptrauth qualifier enabled!
+// expected-warning at -1 {{__ptrauth qualifier enabled!}}
+#endif
+
+ at interface Foo
+// expected-warning at -1 {{class 'Foo' defined without specifying a base class}}
+// expected-note at -2 {{add a super class to fix this problem}}
+
+ at property void *__ptrauth(1, 1, 1) invalid1;
+// expected-error at -1 {{properties may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}}
+
+ at property void *__ptrauth(1, 0, 1) invalid2;
+// expected-error at -1 {{properties may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}}
+
+- (void *__ptrauth(1, 1, 1))invalid5;
+// expected-error at -1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}}
+
+- (void *__ptrauth(1, 0, 1))invalid6;
+// expected-error at -1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}}
+
+- (void)invalid9:(void *__ptrauth(1, 1, 1))a;
+// expected-error at -1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}}
+// expected-note at -2 {{method 'invalid9:' declared here}}
+
+- (void)invalid10:(void *__ptrauth(1, 0, 1))a;
+// expected-error at -1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}}
+// expected-note at -2 {{method 'invalid10:' declared here}}
+
+ at end
+
+ at implementation Foo
+// expected-warning at -1 2{{method definition for}}
+
+- (void *__ptrauth(1, 1, 1))invalid13 {
+// expected-error at -1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}}
+ return 0;
+}
+
+- (void *__ptrauth(1, 0, 1))invalid14 {
+// expected-error at -1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}}
+ return 0;
+}
+
+- (void)invalid17:(void *__ptrauth(1, 1, 1))a {
+// expected-error at -1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}}
+}
+
+- (void)invalid18:(void *__ptrauth(1, 0, 1))a {
+// expected-error at -1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}}
+}
+
+ at end
>From d0021eaedd31eac054ab2782be4c5f37b7416dd1 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Fri, 26 Jul 2024 17:04:08 -0700
Subject: [PATCH 2/5] Fix coding style violations
---
clang/include/clang/Sema/Sema.h | 4 +-
clang/lib/AST/ItaniumMangle.cpp | 8 +--
clang/lib/AST/TypePrinter.cpp | 8 +--
clang/lib/CodeGen/CGDecl.cpp | 12 ++--
clang/lib/CodeGen/CGExpr.cpp | 28 ++++----
clang/lib/CodeGen/CGExprConstant.cpp | 8 +--
clang/lib/CodeGen/CGExprScalar.cpp | 16 ++---
clang/lib/CodeGen/CGPointerAuth.cpp | 98 ++++++++++++++--------------
clang/lib/CodeGen/CodeGenFunction.h | 32 ++++-----
clang/lib/Parse/ParseDecl.cpp | 20 +++---
clang/lib/Sema/SemaChecking.cpp | 52 +++++++--------
clang/lib/Sema/SemaType.cpp | 48 +++++++-------
12 files changed, 167 insertions(+), 167 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 211d3941921ff..673862e4711a4 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3468,8 +3468,8 @@ class Sema final : public SemaBase {
PADAK_ExtraDiscPtrAuth,
};
- bool checkPointerAuthDiscriminatorArg(Expr *arg, PointerAuthDiscArgKind kind,
- unsigned &intVal);
+ bool checkPointerAuthDiscriminatorArg(Expr *Arg, PointerAuthDiscArgKind Kind,
+ unsigned &IntVal);
/// Diagnose function specifiers on a declaration of an identifier that
/// does not identify a function.
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 1b78eeb29f9da..c6c92fb780c18 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2823,7 +2823,7 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals, const DependentAddressSp
mangleVendorQualifier("__unaligned");
// __ptrauth. Note that this is parameterized.
- if (auto ptrauth = Quals.getPointerAuth()) {
+ if (auto PtrAuth = Quals.getPointerAuth()) {
mangleVendorQualifier("__ptrauth");
// For now, since we only allow non-dependent arguments, we can just
// inline the mangling of those arguments as literals. We treat the
@@ -2831,13 +2831,13 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals, const DependentAddressSp
// address-discriminated argument as 'bool'.
Out << "I"
"Lj"
- << ptrauth.getKey()
+ << PtrAuth.getKey()
<< "E"
"Lb"
- << unsigned(ptrauth.isAddressDiscriminated())
+ << unsigned(PtrAuth.isAddressDiscriminated())
<< "E"
"Lj"
- << ptrauth.getExtraDiscriminator()
+ << PtrAuth.getExtraDiscriminator()
<< "E"
"E";
}
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index e5fd7b47cb9c2..3598936cd042f 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2470,8 +2470,8 @@ bool Qualifiers::isEmptyWhenPrinted(const PrintingPolicy &Policy) const {
if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime))
return false;
- if (auto pointerAuth = getPointerAuth())
- if (!pointerAuth.isEmptyWhenPrinted(Policy))
+ if (auto PointerAuth = getPointerAuth())
+ if (!PointerAuth.isEmptyWhenPrinted(Policy))
return false;
return true;
@@ -2580,12 +2580,12 @@ void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy,
}
}
- if (auto pointerAuth = getPointerAuth()) {
+ if (auto PointerAuth = getPointerAuth()) {
if (addSpace)
OS << ' ';
addSpace = true;
- pointerAuth.print(OS, Policy);
+ PointerAuth.print(OS, Policy);
}
if (appendSpaceIfNonEmpty && addSpace)
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 39330853ce705..c81ad78425c03 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -795,17 +795,17 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
LValue lvalue, bool capturedByInit) {
Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime();
if (!lifetime) {
- llvm::Value *value;
- if (auto ptrauth = lvalue.getQuals().getPointerAuth()) {
- value = EmitPointerAuthQualify(ptrauth, init, lvalue.getAddress());
+ llvm::Value *Value;
+ if (auto PtrAuth = lvalue.getQuals().getPointerAuth()) {
+ Value = EmitPointerAuthQualify(PtrAuth, init, lvalue.getAddress());
lvalue.getQuals().removePointerAuth();
} else {
- value = EmitScalarExpr(init);
+ Value = EmitScalarExpr(init);
}
if (capturedByInit)
drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D));
- EmitNullabilityCheck(lvalue, value, init->getExprLoc());
- EmitStoreThroughLValue(RValue::get(value), lvalue, true);
+ EmitNullabilityCheck(lvalue, Value, init->getExprLoc());
+ EmitStoreThroughLValue(RValue::get(Value), lvalue, true);
return;
}
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 43dab7b41b8a1..0d43f824e489e 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2191,10 +2191,10 @@ RValue CodeGenFunction::EmitLoadOfAnyValue(LValue LV, AggValueSlot Slot,
/// returning the rvalue.
RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
// Load from __ptrauth.
- if (auto ptrauth = LV.getQuals().getPointerAuth()) {
+ if (auto PtrAuth = LV.getQuals().getPointerAuth()) {
LV.getQuals().removePointerAuth();
auto value = EmitLoadOfLValue(LV, Loc).getScalarVal();
- return RValue::get(EmitPointerAuthUnqualify(ptrauth, value, LV.getType(),
+ return RValue::get(EmitPointerAuthUnqualify(PtrAuth, value, LV.getType(),
LV.getAddress(),
/*known nonnull*/ false));
}
@@ -2427,8 +2427,8 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
}
// Handle __ptrauth qualification by re-signing the value.
- if (auto pointerAuth = Dst.getQuals().getPointerAuth()) {
- Src = RValue::get(EmitPointerAuthQualify(pointerAuth, Src.getScalarVal(),
+ if (auto PointerAuth = Dst.getQuals().getPointerAuth()) {
+ Src = RValue::get(EmitPointerAuthQualify(PointerAuth, Src.getScalarVal(),
Dst.getType(), Dst.getAddress(),
/*known nonnull*/ false));
}
@@ -5574,21 +5574,21 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) {
// Try to remember the original __ptrauth qualifier for loads of
// function pointers.
if (ICE->getCastKind() == CK_LValueToRValue) {
- auto subExpr = ICE->getSubExpr();
- if (auto ptrType = subExpr->getType()->getAs<PointerType>()) {
- auto result = EmitOrigPointerRValue(E);
+ auto *SubExpr = ICE->getSubExpr();
+ if (auto *PtrType = SubExpr->getType()->getAs<PointerType>()) {
+ auto Result = EmitOrigPointerRValue(E);
- QualType functionType = ptrType->getPointeeType();
- assert(functionType->isFunctionType());
+ QualType FunctionType = PtrType->getPointeeType();
+ assert(FunctionType->isFunctionType());
GlobalDecl GD;
if (const auto *VD =
dyn_cast_or_null<VarDecl>(E->getReferencedDeclOfCallee())) {
GD = GlobalDecl(VD);
}
- CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), GD);
- CGCallee callee(calleeInfo, result.first, result.second);
- return callee;
+ CGCalleeInfo CalleeInfo(FunctionType->getAs<FunctionProtoType>(), GD);
+ CGCallee Callee(CalleeInfo, Result.first, Result.second);
+ return Callee;
}
}
@@ -5654,12 +5654,12 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
switch (getEvaluationKind(E->getType())) {
case TEK_Scalar: {
- if (auto ptrauth = E->getLHS()->getType().getPointerAuth()) {
+ if (auto PtrAuth = E->getLHS()->getType().getPointerAuth()) {
LValue LV = EmitCheckedLValue(E->getLHS(), TCK_Store);
LValue CopiedLV = LV;
CopiedLV.getQuals().removePointerAuth();
llvm::Value *RV =
- EmitPointerAuthQualify(ptrauth, E->getRHS(), CopiedLV.getAddress());
+ EmitPointerAuthQualify(PtrAuth, E->getRHS(), CopiedLV.getAddress());
EmitNullabilityCheck(CopiedLV, RV, E->getExprLoc());
EmitStoreThroughLValue(RValue::get(RV), CopiedLV);
return LV;
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index a66e937c9eabc..9cc80c4385d29 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2068,9 +2068,9 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() {
}
// Apply pointer-auth signing from the destination type.
- if (auto pointerAuth = DestType.getPointerAuth()) {
+ if (auto PointerAuth = DestType.getPointerAuth()) {
if (!result.HasDestPointerAuth) {
- value = Emitter.tryEmitConstantSignedPointer(value, pointerAuth);
+ value = Emitter.tryEmitConstantSignedPointer(value, PointerAuth);
if (!value)
return nullptr;
}
@@ -2119,9 +2119,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
return CGM.GetWeakRefReference(D).getPointer();
auto PtrAuthSign = [&](llvm::Constant *C) {
- if (auto pointerAuth = DestType.getPointerAuth()) {
+ if (auto PointerAuth = DestType.getPointerAuth()) {
C = applyOffset(C);
- C = Emitter.tryEmitConstantSignedPointer(C, pointerAuth);
+ C = Emitter.tryEmitConstantSignedPointer(C, PointerAuth);
return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true);
}
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 05c76cf85581c..be2338a1df67d 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2203,15 +2203,15 @@ static bool isDeclRefKnownNonNull(CodeGenFunction &CGF, const ValueDecl *D) {
static bool isLValueKnownNonNull(CodeGenFunction &CGF, const Expr *E) {
E = E->IgnoreParens();
- if (auto UO = dyn_cast<UnaryOperator>(E)) {
+ if (auto *UO = dyn_cast<UnaryOperator>(E)) {
if (UO->getOpcode() == UO_Deref) {
return CGF.isPointerKnownNonNull(UO->getSubExpr());
}
}
- if (auto DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
return isDeclRefKnownNonNull(CGF, DRE->getDecl());
- } else if (auto ME = dyn_cast<MemberExpr>(E)) {
+ } else if (auto *ME = dyn_cast<MemberExpr>(E)) {
if (isa<FieldDecl>(ME->getMemberDecl()))
return true;
return isDeclRefKnownNonNull(CGF, ME->getMemberDecl());
@@ -2230,13 +2230,13 @@ bool CodeGenFunction::isPointerKnownNonNull(const Expr *E) {
if (isa<CXXThisExpr>(E))
return true;
- if (auto UO = dyn_cast<UnaryOperator>(E)) {
+ if (auto *UO = dyn_cast<UnaryOperator>(E)) {
if (UO->getOpcode() == UO_AddrOf) {
return isLValueKnownNonNull(*this, UO->getSubExpr());
}
}
- if (auto CE = dyn_cast<CastExpr>(E)) {
+ if (auto *CE = dyn_cast<CastExpr>(E)) {
if (CE->getCastKind() == CK_FunctionToPointerDecay ||
CE->getCastKind() == CK_ArrayToPointerDecay) {
return isLValueKnownNonNull(*this, CE->getSubExpr());
@@ -4850,17 +4850,17 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
Value *RHS;
LValue LHS;
- if (auto ptrauth = E->getLHS()->getType().getPointerAuth()) {
+ if (auto PtrAuth = E->getLHS()->getType().getPointerAuth()) {
LValue LV = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store);
LV.getQuals().removePointerAuth();
llvm::Value *RV =
- CGF.EmitPointerAuthQualify(ptrauth, E->getRHS(), LV.getAddress());
+ CGF.EmitPointerAuthQualify(PtrAuth, E->getRHS(), LV.getAddress());
CGF.EmitNullabilityCheck(LV, RV, E->getExprLoc());
CGF.EmitStoreThroughLValue(RValue::get(RV), LV);
if (Ignore)
return nullptr;
- RV = CGF.EmitPointerAuthUnqualify(ptrauth, RV, LV.getType(),
+ RV = CGF.EmitPointerAuthUnqualify(PtrAuth, RV, LV.getType(),
LV.getAddress(), /*nonnull*/ false);
return RV;
}
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp
index 5cd9cba6b41d1..eec46eeab1f45 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -194,16 +194,16 @@ CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) {
}
static std::pair<llvm::Value *, CGPointerAuthInfo>
-emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &lv,
- SourceLocation loc) {
- auto value = CGF.EmitLoadOfScalar(lv, loc);
- CGPointerAuthInfo authInfo;
- if (auto ptrauth = lv.getQuals().getPointerAuth()) {
- authInfo = CGF.EmitPointerAuthInfo(ptrauth, lv.getAddress());
+emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &LV,
+ SourceLocation Loc) {
+ auto *Value = CGF.EmitLoadOfScalar(LV, Loc);
+ CGPointerAuthInfo AuthInfo;
+ if (auto PtrAuth = LV.getQuals().getPointerAuth()) {
+ AuthInfo = CGF.EmitPointerAuthInfo(PtrAuth, LV.getAddress());
} else {
- authInfo = getPointerAuthInfoForType(CGF.CGM, lv.getType());
+ AuthInfo = getPointerAuthInfoForType(CGF.CGM, LV.getType());
}
- return {value, authInfo};
+ return {Value, AuthInfo};
}
std::pair<llvm::Value *, CGPointerAuthInfo>
@@ -211,82 +211,82 @@ CodeGenFunction::EmitOrigPointerRValue(const Expr *E) {
assert(E->getType()->isSignableType());
E = E->IgnoreParens();
- if (auto load = dyn_cast<ImplicitCastExpr>(E)) {
- if (load->getCastKind() == CK_LValueToRValue) {
- E = load->getSubExpr()->IgnoreParens();
+ if (auto *Load = dyn_cast<ImplicitCastExpr>(E)) {
+ if (Load->getCastKind() == CK_LValueToRValue) {
+ E = Load->getSubExpr()->IgnoreParens();
// We're semantically required to not emit loads of certain DREs naively.
- if (auto refExpr = dyn_cast<DeclRefExpr>(const_cast<Expr *>(E))) {
- if (auto result = tryEmitAsConstant(refExpr)) {
+ if (auto *RefExpr = dyn_cast<DeclRefExpr>(const_cast<Expr *>(E))) {
+ if (auto Result = tryEmitAsConstant(RefExpr)) {
// Fold away a use of an intermediate variable.
- if (!result.isReference())
- return {result.getValue(),
- getPointerAuthInfoForType(CGM, refExpr->getType())};
+ if (!Result.isReference())
+ return {Result.getValue(),
+ getPointerAuthInfoForType(CGM, RefExpr->getType())};
// Fold away a use of an intermediate reference.
- auto lv = result.getReferenceLValue(*this, refExpr);
- return emitLoadOfOrigPointerRValue(*this, lv, refExpr->getLocation());
+ auto LV = Result.getReferenceLValue(*this, RefExpr);
+ return emitLoadOfOrigPointerRValue(*this, LV, RefExpr->getLocation());
}
}
// Otherwise, load and use the pointer
- auto lv = EmitCheckedLValue(E, CodeGenFunction::TCK_Load);
- return emitLoadOfOrigPointerRValue(*this, lv, E->getExprLoc());
+ auto LV = EmitCheckedLValue(E, CodeGenFunction::TCK_Load);
+ return emitLoadOfOrigPointerRValue(*this, LV, E->getExprLoc());
}
}
// Emit direct references to functions without authentication.
- if (auto DRE = dyn_cast<DeclRefExpr>(E)) {
- if (auto FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
+ if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
return {CGM.getRawFunctionPointer(FD), CGPointerAuthInfo()};
}
- } else if (auto ME = dyn_cast<MemberExpr>(E)) {
- if (auto FD = dyn_cast<FunctionDecl>(ME->getMemberDecl())) {
+ } else if (auto *ME = dyn_cast<MemberExpr>(E)) {
+ if (auto *FD = dyn_cast<FunctionDecl>(ME->getMemberDecl())) {
EmitIgnoredExpr(ME->getBase());
return {CGM.getRawFunctionPointer(FD), CGPointerAuthInfo()};
}
}
// Fallback: just use the normal rules for the type.
- auto value = EmitScalarExpr(E);
- return {value, getPointerAuthInfoForType(CGM, E->getType())};
+ auto *Value = EmitScalarExpr(E);
+ return {Value, getPointerAuthInfoForType(CGM, E->getType())};
}
llvm::Value *
-CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier,
+CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier DestQualifier,
const Expr *E,
- Address destStorageAddress) {
- assert(destQualifier);
+ Address DestStorageAddress) {
+ assert(DestQualifier);
- auto src = EmitOrigPointerRValue(E);
- auto value = src.first;
- auto curAuthInfo = src.second;
+ auto Src = EmitOrigPointerRValue(E);
+ auto *Value = Src.first;
+ auto CurAuthInfo = Src.second;
- auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress);
- return emitPointerAuthResign(value, E->getType(), curAuthInfo, destAuthInfo,
+ auto DestAuthInfo = EmitPointerAuthInfo(DestQualifier, DestStorageAddress);
+ return emitPointerAuthResign(Value, E->getType(), CurAuthInfo, DestAuthInfo,
isPointerKnownNonNull(E));
}
llvm::Value *CodeGenFunction::EmitPointerAuthQualify(
- PointerAuthQualifier destQualifier, llvm::Value *value,
- QualType pointerType, Address destStorageAddress, bool isKnownNonNull) {
- assert(destQualifier);
+ PointerAuthQualifier DestQualifier, llvm::Value *Value,
+ QualType PointerType, Address DestStorageAddress, bool IsKnownNonNull) {
+ assert(DestQualifier);
- auto curAuthInfo = getPointerAuthInfoForType(CGM, pointerType);
- auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress);
- return emitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo,
- isKnownNonNull);
+ auto CurAuthInfo = getPointerAuthInfoForType(CGM, PointerType);
+ auto DestAuthInfo = EmitPointerAuthInfo(DestQualifier, DestStorageAddress);
+ return emitPointerAuthResign(Value, PointerType, CurAuthInfo, DestAuthInfo,
+ IsKnownNonNull);
}
llvm::Value *CodeGenFunction::EmitPointerAuthUnqualify(
- PointerAuthQualifier curQualifier, llvm::Value *value, QualType pointerType,
- Address curStorageAddress, bool isKnownNonNull) {
- assert(curQualifier);
-
- auto curAuthInfo = EmitPointerAuthInfo(curQualifier, curStorageAddress);
- auto destAuthInfo = getPointerAuthInfoForType(CGM, pointerType);
- return emitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo,
- isKnownNonNull);
+ PointerAuthQualifier CurQualifier, llvm::Value *Value, QualType PointerType,
+ Address CurStorageAddress, bool IsKnownNonNull) {
+ assert(CurQualifier);
+
+ auto CurAuthInfo = EmitPointerAuthInfo(CurQualifier, CurStorageAddress);
+ auto DestAuthInfo = getPointerAuthInfoForType(CGM, PointerType);
+ return emitPointerAuthResign(Value, PointerType, CurAuthInfo, DestAuthInfo,
+ IsKnownNonNull);
}
static bool isZeroConstant(const llvm::Value *Value) {
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index e7729207cc6a6..91bfe551268f3 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4457,22 +4457,22 @@ class CodeGenFunction : public CodeGenTypeCache {
const CGPointerAuthInfo &Info,
SmallVectorImpl<llvm::OperandBundleDef> &Bundles);
- CGPointerAuthInfo EmitPointerAuthInfo(PointerAuthQualifier qualifier,
- Address storageAddress);
- llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier qualifier,
- llvm::Value *pointer, QualType valueType,
- Address storageAddress,
- bool isKnownNonNull);
- llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier qualifier,
- const Expr *pointerExpr,
- Address storageAddress);
- llvm::Value *EmitPointerAuthUnqualify(PointerAuthQualifier qualifier,
- llvm::Value *pointer,
- QualType pointerType,
- Address storageAddress,
- bool isKnownNonNull);
- void EmitPointerAuthCopy(PointerAuthQualifier qualifier, QualType type,
- Address destField, Address srcField);
+ CGPointerAuthInfo EmitPointerAuthInfo(PointerAuthQualifier Qualifier,
+ Address StorageAddress);
+ llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier Qualifier,
+ llvm::Value *Pointer, QualType ValueType,
+ Address StorageAddress,
+ bool IsKnownNonNull);
+ llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier Qualifier,
+ const Expr *PointerExpr,
+ Address StorageAddress);
+ llvm::Value *EmitPointerAuthUnqualify(PointerAuthQualifier Qualifier,
+ llvm::Value *Pointer,
+ QualType PointerType,
+ Address StorageAddress,
+ bool IsKnownNonNull);
+ void EmitPointerAuthCopy(PointerAuthQualifier Qualifier, QualType Type,
+ Address DestField, Address SrcField);
std::pair<llvm::Value *, CGPointerAuthInfo>
EmitOrigPointerRValue(const Expr *E);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 2a14a6a34eede..1b0b63dc9f200 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3373,29 +3373,29 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
void Parser::ParsePtrauthQualifier(ParsedAttributes &attrs) {
assert(Tok.is(tok::kw___ptrauth));
- IdentifierInfo *kwName = Tok.getIdentifierInfo();
- SourceLocation kwLoc = ConsumeToken();
+ IdentifierInfo *KwName = Tok.getIdentifierInfo();
+ SourceLocation KwLoc = ConsumeToken();
BalancedDelimiterTracker T(*this, tok::l_paren);
if (T.expectAndConsume())
return;
- ArgsVector argExprs;
+ ArgsVector ArgExprs;
do {
- ExprResult expr = ParseAssignmentExpression();
- if (expr.isInvalid()) {
+ ExprResult ER = ParseAssignmentExpression();
+ if (ER.isInvalid()) {
T.skipToEnd();
return;
}
- argExprs.push_back(expr.get());
+ ArgExprs.push_back(ER.get());
} while (TryConsumeToken(tok::comma));
T.consumeClose();
- SourceLocation endLoc = T.getCloseLocation();
+ SourceLocation EndLoc = T.getCloseLocation();
- attrs.addNew(kwName, SourceRange(kwLoc, endLoc),
- /*scope*/ nullptr, SourceLocation(), argExprs.data(),
- argExprs.size(),
+ attrs.addNew(KwName, SourceRange(KwLoc, EndLoc),
+ /*scope*/ nullptr, SourceLocation(), ArgExprs.data(),
+ ArgExprs.size(),
ParsedAttr::Form::Keyword(/*IsAlignAs=*/false,
/*IsRegularKeywordAttribute=*/false));
}
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 4aefc12789c13..c2d8e03285715 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1540,51 +1540,51 @@ bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) {
return false;
}
-bool Sema::checkPointerAuthDiscriminatorArg(Expr *arg,
- PointerAuthDiscArgKind kind,
- unsigned &intVal) {
- if (!arg) {
- intVal = 0;
+bool Sema::checkPointerAuthDiscriminatorArg(Expr *Arg,
+ PointerAuthDiscArgKind Kind,
+ unsigned &IntVal) {
+ if (!Arg) {
+ IntVal = 0;
return true;
}
- std::optional<llvm::APSInt> result = arg->getIntegerConstantExpr(Context);
- if (!result) {
- Diag(arg->getExprLoc(), diag::err_ptrauth_arg_not_ice);
+ std::optional<llvm::APSInt> Result = Arg->getIntegerConstantExpr(Context);
+ if (!Result) {
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_arg_not_ice);
return false;
}
- unsigned max;
- bool isAddrDiscArg = false;
+ unsigned Max;
+ bool IsAddrDiscArg = false;
- switch (kind) {
+ switch (Kind) {
case PADAK_AddrDiscPtrAuth:
- max = 1;
- isAddrDiscArg = true;
+ Max = 1;
+ IsAddrDiscArg = true;
break;
case PADAK_ExtraDiscPtrAuth:
- max = PointerAuthQualifier::MaxDiscriminator;
+ Max = PointerAuthQualifier::MaxDiscriminator;
break;
};
- if (*result < 0 || *result > max) {
- llvm::SmallString<32> value;
+ if (*Result < 0 || *Result > Max) {
+ llvm::SmallString<32> Value;
{
- llvm::raw_svector_ostream str(value);
- str << *result;
+ llvm::raw_svector_ostream str(Value);
+ str << *Result;
}
- if (isAddrDiscArg)
- Diag(arg->getExprLoc(), diag::err_ptrauth_address_discrimination_invalid)
- << value;
+ if (IsAddrDiscArg)
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_address_discrimination_invalid)
+ << Value;
else
- Diag(arg->getExprLoc(), diag::err_ptrauth_extra_discriminator_invalid)
- << value << max;
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_extra_discriminator_invalid)
+ << Value << Max;
return false;
};
- intVal = result->getZExtValue();
+ IntVal = Result->getZExtValue();
return true;
}
@@ -4217,8 +4217,8 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
<< FirstArg->getType() << 0 << FirstArg->getSourceRange();
return ExprError();
}
- auto pointerAuth = ValType.getPointerAuth();
- if (pointerAuth && pointerAuth.isAddressDiscriminated()) {
+ auto PointerAuth = ValType.getPointerAuth();
+ if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
Diag(FirstArg->getBeginLoc(),
diag::err_atomic_op_needs_non_address_discriminated_pointer)
<< 1 << ValType << FirstArg->getSourceRange();
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index c4da0f237c833..515f874639509 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8229,51 +8229,51 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr,
}
/// Handle the __ptrauth qualifier.
-static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &type,
+static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
const ParsedAttr &attr, Sema &S) {
- auto attributeName = attr.getAttrName()->getName();
+ auto AttributeName = attr.getAttrName()->getName();
if (attr.getNumArgs() < 1 || attr.getNumArgs() > 3) {
S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_bad_arg_count)
- << attributeName;
+ << AttributeName;
attr.setInvalid();
return;
}
- Expr *keyArg = attr.getArgAsExpr(0);
- Expr *isAddressDiscriminatedArg =
+ Expr *KeyArg = attr.getArgAsExpr(0);
+ Expr *IsAddressDiscriminatedArg =
attr.getNumArgs() >= 2 ? attr.getArgAsExpr(1) : nullptr;
- Expr *extraDiscriminatorArg =
+ Expr *ExtraDiscriminatorArg =
attr.getNumArgs() >= 3 ? attr.getArgAsExpr(2) : nullptr;
- unsigned key;
- if (S.checkConstantPointerAuthKey(keyArg, key)) {
+ unsigned Key;
+ if (S.checkConstantPointerAuthKey(KeyArg, Key)) {
attr.setInvalid();
return;
}
- assert(key <= PointerAuthQualifier::MaxKey && "ptrauth key is out of range");
+ assert(Key <= PointerAuthQualifier::MaxKey && "ptrauth key is out of range");
- bool isInvalid = false;
- unsigned isAddressDiscriminated, extraDiscriminator;
- isInvalid |= !S.checkPointerAuthDiscriminatorArg(isAddressDiscriminatedArg,
+ bool IsInvalid = false;
+ unsigned IsAddressDiscriminated, ExtraDiscriminator;
+ IsInvalid |= !S.checkPointerAuthDiscriminatorArg(IsAddressDiscriminatedArg,
Sema::PADAK_AddrDiscPtrAuth,
- isAddressDiscriminated);
- isInvalid |= !S.checkPointerAuthDiscriminatorArg(
- extraDiscriminatorArg, Sema::PADAK_ExtraDiscPtrAuth, extraDiscriminator);
+ IsAddressDiscriminated);
+ IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
+ ExtraDiscriminatorArg, Sema::PADAK_ExtraDiscPtrAuth, ExtraDiscriminator);
- if (isInvalid) {
+ if (IsInvalid) {
attr.setInvalid();
return;
}
- if (!type->isSignableType() && !type->isDependentType()) {
- S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << type;
+ if (!T->isSignableType() && !T->isDependentType()) {
+ S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << T;
attr.setInvalid();
return;
}
- if (type.getPointerAuth()) {
+ if (T.getPointerAuth()) {
S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_redundant)
- << type << attr.getAttrName()->getName();
+ << T << attr.getAttrName()->getName();
attr.setInvalid();
return;
}
@@ -8284,12 +8284,12 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &type,
return;
}
- assert((!isAddressDiscriminatedArg || isAddressDiscriminated <= 1) &&
+ assert((!IsAddressDiscriminatedArg || IsAddressDiscriminated <= 1) &&
"address discriminator arg should be either 0 or 1");
- PointerAuthQualifier qual = PointerAuthQualifier::Create(
- key, isAddressDiscriminated, extraDiscriminator,
+ PointerAuthQualifier Qual = PointerAuthQualifier::Create(
+ Key, IsAddressDiscriminated, ExtraDiscriminator,
PointerAuthenticationMode::SignAndAuth, false, false);
- type = S.Context.getPointerAuthType(type, qual);
+ T = S.Context.getPointerAuthType(T, Qual);
}
/// HandleArmSveVectorBitsTypeAttr - The "arm_sve_vector_bits" attribute is
>From 66c1f79e9a9dd4a1c521ddc5956e533ecba77c36 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Fri, 26 Jul 2024 17:14:01 -0700
Subject: [PATCH 3/5] Revert unnecessary change
---
clang/lib/CodeGen/CGExprConstant.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 9cc80c4385d29..8f3266dfbd094 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2352,9 +2352,6 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
EnablePtrAuthFunctionTypeDiscrimination)
.tryEmit();
case APValue::Int:
- if (DestType.getPointerAuth() && Value.getInt() != 0) {
- return nullptr;
- }
return llvm::ConstantInt::get(CGM.getLLVMContext(), Value.getInt());
case APValue::FixedPoint:
return llvm::ConstantInt::get(CGM.getLLVMContext(),
>From 73a242284699fa1f5fd491843eb85986a971a401 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Mon, 29 Jul 2024 22:22:32 -0700
Subject: [PATCH 4/5] Fix coding style violations
Improve docs and comments. Add a test for the type printer.
---
clang/docs/PointerAuthentication.rst | 46 +++++++
clang/include/clang/AST/Type.h | 2 +-
clang/include/clang/Basic/AttrDocs.td | 122 +-----------------
clang/lib/AST/ItaniumMangle.cpp | 2 +-
clang/lib/AST/TypePrinter.cpp | 4 +-
clang/lib/CodeGen/CGDebugInfo.cpp | 6 +-
clang/lib/CodeGen/CGDecl.cpp | 2 +-
clang/lib/CodeGen/CGExpr.cpp | 7 +-
clang/lib/CodeGen/CGExprConstant.cpp | 4 +-
clang/lib/CodeGen/CGExprScalar.cpp | 2 +-
clang/lib/CodeGen/CGPointerAuth.cpp | 16 +--
clang/lib/Sema/SemaChecking.cpp | 4 +-
clang/test/AST/ast-dump-ptrauth-json.cpp | 2 +
clang/test/Preprocessor/ptrauth_feature.c | 2 -
...mic-ops-ptrauth.c => ptrauth-atomic-ops.c} | 0
15 files changed, 70 insertions(+), 151 deletions(-)
rename clang/test/Sema/{atomic-ops-ptrauth.c => ptrauth-atomic-ops.c} (100%)
diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index 68674f318c84f..41818d43ac687 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -280,6 +280,52 @@ a number of different tests.
normal interface. This may be true even on targets where pointer
authentication is not enabled by default.
+__ptrauth Qualifier
+^^^^^^^^^^^^^^^^^^^
+
+``__ptrauth(key, address, discriminator)`` is an extended type
+qualifier which causes so-qualified objects to hold pointers signed using the
+specified schema rather than the default schema for such types.
+
+In the current implementation in Clang, the qualified type must be a C pointer
+type, either to a function or to an object. It currently cannot be an
+Objective-C pointer type, a C++ reference type, or a block pointer type; these
+restrictions may be lifted in the future.
+
+The qualifier's operands are as follows:
+
+- ``key`` - an expression evaluating to a key value from ``<ptrauth.h>``; must
+ be a constant expression
+
+- ``address`` - whether to use address diversity (1) or not (0); must be
+ a constant expression with one of these two values
+
+- ``discriminator`` - a constant discriminator; must be a constant expression
+
+See `Discriminators`_ for more information about discriminators.
+
+Currently the operands must be constant-evaluable even within templates. In the
+future this restriction may be lifted to allow value-dependent expressions as
+long as they instantiate to a constant expression.
+
+Consistent with the ordinary C/C++ rule for parameters, top-level ``__ptrauth``
+qualifiers on a parameter (after parameter type adjustment) are ignored when
+deriving the type of the function. The parameter will be passed using the
+default ABI for the unqualified pointer type.
+
+If ``x`` is an object of type ``__ptrauth(key, address, discriminator) T``,
+then the signing schema of the value stored in ``x`` is a key of ``key`` and
+a discriminator determined as follows:
+
+- if ``address`` is 0, then the discriminator is ``discriminator``;
+
+- if ``address`` is 1 and ``discriminator`` is 0, then the discriminator is
+ ``&x``; otherwise
+
+- if ``address`` is 1 and ``discriminator`` is non-zero, then the discriminator
+ is ``ptrauth_blend_discriminator(&x, discriminator)``; see
+ `ptrauth_blend_discriminator`_.
+
``<ptrauth.h>``
~~~~~~~~~~~~~~~
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index b84d808410583..2fb17f352f1b3 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1468,7 +1468,7 @@ class QualType {
}
bool hasAddressDiscriminatedPointerAuth() const {
- if (auto ptrauth = getPointerAuth())
+ if (PointerAuthQualifier ptrauth = getPointerAuth())
return ptrauth.isAddressDiscriminated();
return false;
}
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 547cb010ebc84..e7b990a2abb8c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1778,129 +1778,11 @@ an ability to alter memory into full control of a process.
The first argument to ``__ptrauth`` is the name of the signing key.
Valid key names for the target are defined in ``<ptrauth.h>``.
-On ARM64, there are four keys:
-
-- ``ptrauth_key_process_independent_data``
-- ``ptrauth_key_process_dependent_data``
-- ``ptrauth_key_process_independent_code``
-- ``ptrauth_key_process_dependent_code``
-
-In general, prefer using a code key for function pointers and a data key
-for object pointers. The ARM64 architecture allows loads and calls to
-execute more efficiently when the pointer is signed with an appropriate
-key. Using code keys only for function pointers also substantially lessens
-the risk of creating a so-called "signing oracle" for function pointers;
-see the general pointer authentication language documentation.
-
-Using a process-dependent key provides stronger protection against
-cross-process attacks. However, it also inhibits certain memory
-optimizations when a shared library is loaded into multiple processes.
-Using a process-independent key also allows signed pointers to be passed
-in shared memory. Note that even the process-independent keys may change
-after a reboot, so signed values should never be serialized.
-
The second argument to ``__ptrauth`` is a flag (0 or 1) specifying whether
-the object should use address discrimination. If only one argument is
-given, the flag defaults to 0. Address discrimination provides strong
-protection against attacks which copy signed pointers around in memory.
-An attacker cannot usefully copy an arbitrary signed pointer over an
-address-discriminated object. Nor can a value taken from an
-address-discriminated object be usefully copied over some other signed
-pointer. However, it is more expensive to copy values from one
-address-discriminated object to another, even if the other arguments to
-``__ptrauth`` are the same, and it is not valid to copy them with
-``memcpy``. It is also not valid to map memory containing an
-address-discriminated object into different places in the address
-space, e.g. with ``mmap``.
+the object should use address discrimination.
The third argument to ``__ptrauth`` is a small non-negative integer
-which allows additional discrimination between objects. Using a
-unique extra discriminator provides strong protection against attacks
-which work by substituting one signed value for another. For example,
-an attacker cannot usefully overwrite an object with a pointer from an
-object using a different extra discriminator; this protection is similar
-to the protection offered by address discrimination. A unique extra
-discriminator also protects against "slide" attacks where an attacker
-alters a pointer instead of altering the memory that the pointer points to.
-The extra discriminator must be a constant expression. On ARM64,
-its value must be between 0 and 65535. If the argument is not provided,
-the default value is 0. It is generally preferable not to use the value 0,
-especially with the process-independent keys, as this combination is used
-in various places in the standard language ABI.
-
-The type qualified by ``__ptrauth`` must be a pointer type. Currently
-only C pointer types are allowed and not block pointers, Objective-C
-object pointers, or C++ references. ``__ptrauth`` is parsed and interpreted
-using the same language rules as qualifiers like ``const`` and ``volatile``.
-For example:
-
-.. code-block:: c
-
- __ptrauth(...) int *ex0; /* invalid: qualifies 'int', which is not a pointer type */
- int * __ptrauth(...) ex1; /* valid: ex1 has qualified type */
- int * __ptrauth(...) *ex2; /* valid: ex2 is a pointer to a qualified object */
-
- typedef int *intp;
- __ptrauth(...) intp ex3; /* valid: ex3 has qualified type */
- intp __ptrauth(...) ex4; /* valid: means the exact same thing as ex3 */
-
-If a ``__ptrauth``-qualified l-value of function pointer type is
-used as the function operand of a call expression, the function pointer
-will be authenticated "atomically" with the call, such that an attacker
-will not be able to corrupt the destination of the call even in the
-presence of undefined behavior. (That is, the compiler must not
-leave an un-signed pointer that it will later unconditionally trust
-in a place where it could be feasibly overwritten by an attacker,
-such as the stack or a callee-save register during an intervening call.
-The compiler is not required to protect against improbable attacks
-such as corruption of the register file, as might occur with a
-corrupted kernel. It also need not guard against jumps to an arbitrary
-place in the instruction stream, since such jumps would require an
-attacker to already fully control the PC.)
-
-If the ABI specifies that a pointer is always signed --- that is,
-if the pointer is a function pointer and the target uses ABI function
-pointer authentication --- then signing and authenticating it as part
-of a load/store actually means resigning it to/from the standard ABI
-signature schema. Similarly, if both operands of a simple assignment
-operator are ``__ptrauth``-qualified, the pointer copied by the
-assignment is resigned from the right-hand operand's schema to the
-left-hand operand's schema. These resigning operations are also done
-"atomically" in the same sense as above.
-
-As a final guarantee, if the right-hand operand of an assignment or
-the expression used to initialize a ``__ptrauth``-qualified object is
-a direct reference to an object or function (e.g. ``&my_var``), the
-signing of that pointer is atomic with the evaluaton of the reference
-in this same sense.
-
-Otherwise, there are no guarantees of atomicity, and it is the
-programmer's responsibility to avoid allowing a store into a
-``__ptrauth``-qualified object to create a potential "signing oracle"
-which an attacker could use to sign an arbitrary pointer of their choice.
-Such oracles are particularly problematic when the signing uses a code
-key because the oracle could potentially be used to allow an attacker
-to construct a validly-signed function pointer, v-table entry, or
-return address that points to an arbitrary instruction, allowing them
-to completely take over the PC. Programmers attempting to use
-``__ptrauth`` to protect a data pointer, or to protect function pointers
-on targets that do not use ABI function pointer authentication, should
-aim to maintain a "chain of authentication" from initialization all
-the way to the point at which the pointer is used. If this is infeasible,
-they should consider using ``ptrauth_sign_generic_data`` instead.
-
-Types that are written in r-value positions, such as return types,
-parameter types, and cast types, may not be ``__ptrauth``-qualified
-at the outermost level. This may be supported in the future.
-
-In C++, the arguments to ``__ptrauth`` may not be instantiation-dependent.
-This may be supported in the future.
-
-This feature may be tested for with ``__has_feature(ptrauth_qualifier)``.
-It is enabled whenever the ``ptrauth`` intrinsics are enabled.
-
-``<ptrauth.h>`` provides predefined qualifiers for various language
-features that implicitly use pointer authentication.
+which allows additional discrimination between objects.
}];
}
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index c6c92fb780c18..0e95517bfa424 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2823,7 +2823,7 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals, const DependentAddressSp
mangleVendorQualifier("__unaligned");
// __ptrauth. Note that this is parameterized.
- if (auto PtrAuth = Quals.getPointerAuth()) {
+ if (PointerAuthQualifier PtrAuth = Quals.getPointerAuth()) {
mangleVendorQualifier("__ptrauth");
// For now, since we only allow non-dependent arguments, we can just
// inline the mangling of those arguments as literals. We treat the
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 3598936cd042f..542cf7502e20e 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2470,7 +2470,7 @@ bool Qualifiers::isEmptyWhenPrinted(const PrintingPolicy &Policy) const {
if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime))
return false;
- if (auto PointerAuth = getPointerAuth())
+ if (PointerAuthQualifier PointerAuth = getPointerAuth())
if (!PointerAuth.isEmptyWhenPrinted(Policy))
return false;
@@ -2580,7 +2580,7 @@ void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy,
}
}
- if (auto PointerAuth = getPointerAuth()) {
+ if (PointerAuthQualifier PointerAuth = getPointerAuth()) {
if (addSpace)
OS << ' ';
addSpace = true;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index ae4026a84601e..fe4106adf56ee 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1025,9 +1025,9 @@ llvm::DIType *CGDebugInfo::CreateQualifiedType(QualType Ty,
Qc.removePointerAuth();
assert(Qc.empty() && "Unknown type qualifier for debug info");
auto *FromTy = getOrCreateType(QualType(T, 0), Unit);
- return DBuilder.createPtrAuthQualifiedType(
- FromTy, Key, IsDiscr, ExtraDiscr, /*IsaPointer=*/IsaPointer,
- /*AuthenticatesNullValues=*/AuthenticatesNullValues);
+ return DBuilder.createPtrAuthQualifiedType(FromTy, Key, IsDiscr,
+ ExtraDiscr, IsaPointer,
+ AuthenticatesNullValues);
} else {
assert(Qc.empty() && "Unknown type qualifier for debug info");
return getOrCreateType(QualType(T, 0), Unit);
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index c81ad78425c03..ef612300b4648 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -796,7 +796,7 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime();
if (!lifetime) {
llvm::Value *Value;
- if (auto PtrAuth = lvalue.getQuals().getPointerAuth()) {
+ if (PointerAuthQualifier PtrAuth = lvalue.getQuals().getPointerAuth()) {
Value = EmitPointerAuthQualify(PtrAuth, init, lvalue.getAddress());
lvalue.getQuals().removePointerAuth();
} else {
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 0d43f824e489e..20caf0211cec1 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2191,7 +2191,7 @@ RValue CodeGenFunction::EmitLoadOfAnyValue(LValue LV, AggValueSlot Slot,
/// returning the rvalue.
RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
// Load from __ptrauth.
- if (auto PtrAuth = LV.getQuals().getPointerAuth()) {
+ if (PointerAuthQualifier PtrAuth = LV.getQuals().getPointerAuth()) {
LV.getQuals().removePointerAuth();
auto value = EmitLoadOfLValue(LV, Loc).getScalarVal();
return RValue::get(EmitPointerAuthUnqualify(PtrAuth, value, LV.getType(),
@@ -2427,7 +2427,7 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
}
// Handle __ptrauth qualification by re-signing the value.
- if (auto PointerAuth = Dst.getQuals().getPointerAuth()) {
+ if (PointerAuthQualifier PointerAuth = Dst.getQuals().getPointerAuth()) {
Src = RValue::get(EmitPointerAuthQualify(PointerAuth, Src.getScalarVal(),
Dst.getType(), Dst.getAddress(),
/*known nonnull*/ false));
@@ -5654,7 +5654,8 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
switch (getEvaluationKind(E->getType())) {
case TEK_Scalar: {
- if (auto PtrAuth = E->getLHS()->getType().getPointerAuth()) {
+ if (PointerAuthQualifier PtrAuth =
+ E->getLHS()->getType().getPointerAuth()) {
LValue LV = EmitCheckedLValue(E->getLHS(), TCK_Store);
LValue CopiedLV = LV;
CopiedLV.getQuals().removePointerAuth();
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 8f3266dfbd094..1aaf81cc41cad 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2068,7 +2068,7 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() {
}
// Apply pointer-auth signing from the destination type.
- if (auto PointerAuth = DestType.getPointerAuth()) {
+ if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth()) {
if (!result.HasDestPointerAuth) {
value = Emitter.tryEmitConstantSignedPointer(value, PointerAuth);
if (!value)
@@ -2119,7 +2119,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
return CGM.GetWeakRefReference(D).getPointer();
auto PtrAuthSign = [&](llvm::Constant *C) {
- if (auto PointerAuth = DestType.getPointerAuth()) {
+ if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth()) {
C = applyOffset(C);
C = Emitter.tryEmitConstantSignedPointer(C, PointerAuth);
return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true);
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index be2338a1df67d..0047ee4dca6fa 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4850,7 +4850,7 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
Value *RHS;
LValue LHS;
- if (auto PtrAuth = E->getLHS()->getType().getPointerAuth()) {
+ if (PointerAuthQualifier PtrAuth = E->getLHS()->getType().getPointerAuth()) {
LValue LV = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store);
LV.getQuals().removePointerAuth();
llvm::Value *RV =
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp
index eec46eeab1f45..a2a014edbba12 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -198,7 +198,7 @@ emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &LV,
SourceLocation Loc) {
auto *Value = CGF.EmitLoadOfScalar(LV, Loc);
CGPointerAuthInfo AuthInfo;
- if (auto PtrAuth = LV.getQuals().getPointerAuth()) {
+ if (PointerAuthQualifier PtrAuth = LV.getQuals().getPointerAuth()) {
AuthInfo = CGF.EmitPointerAuthInfo(PtrAuth, LV.getAddress());
} else {
AuthInfo = getPointerAuthInfoForType(CGF.CGM, LV.getType());
@@ -206,6 +206,8 @@ emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &LV,
return {Value, AuthInfo};
}
+/// Retrieve a pointer rvalue and its ptrauth info. When possible, avoid
+/// needlessly resigning the pointer.
std::pair<llvm::Value *, CGPointerAuthInfo>
CodeGenFunction::EmitOrigPointerRValue(const Expr *E) {
assert(E->getType()->isSignableType());
@@ -235,18 +237,6 @@ CodeGenFunction::EmitOrigPointerRValue(const Expr *E) {
}
}
- // Emit direct references to functions without authentication.
- if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
- if (auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
- return {CGM.getRawFunctionPointer(FD), CGPointerAuthInfo()};
- }
- } else if (auto *ME = dyn_cast<MemberExpr>(E)) {
- if (auto *FD = dyn_cast<FunctionDecl>(ME->getMemberDecl())) {
- EmitIgnoredExpr(ME->getBase());
- return {CGM.getRawFunctionPointer(FD), CGPointerAuthInfo()};
- }
- }
-
// Fallback: just use the normal rules for the type.
auto *Value = EmitScalarExpr(E);
return {Value, getPointerAuthInfoForType(CGM, E->getType())};
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index c2d8e03285715..f7d6c318924bb 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3843,7 +3843,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
return ExprError();
}
- auto PointerAuth = AtomTy.getPointerAuth();
+ PointerAuthQualifier PointerAuth = AtomTy.getPointerAuth();
if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
Diag(ExprRange.getBegin(),
diag::err_atomic_op_needs_non_address_discriminated_pointer)
@@ -4217,7 +4217,7 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
<< FirstArg->getType() << 0 << FirstArg->getSourceRange();
return ExprError();
}
- auto PointerAuth = ValType.getPointerAuth();
+ PointerAuthQualifier PointerAuth = ValType.getPointerAuth();
if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
Diag(FirstArg->getBeginLoc(),
diag::err_atomic_op_needs_non_address_discriminated_pointer)
diff --git a/clang/test/AST/ast-dump-ptrauth-json.cpp b/clang/test/AST/ast-dump-ptrauth-json.cpp
index 125cda0cff53a..a85b1d8557335 100644
--- a/clang/test/AST/ast-dump-ptrauth-json.cpp
+++ b/clang/test/AST/ast-dump-ptrauth-json.cpp
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -std=c++11 -ast-dump=json %s | FileCheck %s
// CHECK: "name": "__builtin_ptrauth_type_discriminator",
+// CHECK: "qualType": "int *__ptrauth(1,1,123)"
int d = __builtin_ptrauth_type_discriminator(int());
+int * __ptrauth(1,1,123) p;
diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c
index 14ef80860d0e9..397f10aa7ab3f 100644
--- a/clang/test/Preprocessor/ptrauth_feature.c
+++ b/clang/test/Preprocessor/ptrauth_feature.c
@@ -85,8 +85,6 @@ void has_ptrauth_type_info_vtable_pointer_discrimination() {}
void no_ptrauth_type_info_vtable_pointer_discrimination() {}
#endif
-#include <ptrauth.h>
-
#if __has_feature(ptrauth_function_pointer_type_discrimination)
// FUNC: has_ptrauth_function_pointer_type_discrimination
void has_ptrauth_function_pointer_type_discrimination() {}
diff --git a/clang/test/Sema/atomic-ops-ptrauth.c b/clang/test/Sema/ptrauth-atomic-ops.c
similarity index 100%
rename from clang/test/Sema/atomic-ops-ptrauth.c
rename to clang/test/Sema/ptrauth-atomic-ops.c
>From f0983226b5eff012c334b8a0b2989966a60101cd Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <ahatanak at gmail.com>
Date: Thu, 1 Aug 2024 11:33:52 -0700
Subject: [PATCH 5/5] Strip qualifiers when evaluating a reference as an rvalue
---
clang/lib/CodeGen/CGExpr.cpp | 2 +-
.../test/CodeGen/ptrauth-qualifier-function.c | 22 ++++++++++++++++++-
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 20caf0211cec1..583da32f7626a 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1750,7 +1750,7 @@ CodeGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) {
if (CEK != CEK_AsReferenceOnly &&
refExpr->EvaluateAsRValue(result, getContext())) {
resultIsReference = false;
- resultType = refExpr->getType();
+ resultType = refExpr->getType().getUnqualifiedType();
// Otherwise, try to evaluate as an l-value.
} else if (CEK != CEK_AsValueOnly &&
diff --git a/clang/test/CodeGen/ptrauth-qualifier-function.c b/clang/test/CodeGen/ptrauth-qualifier-function.c
index f6cde0cd96e62..6b5b2c707b5c4 100644
--- a/clang/test/CodeGen/ptrauth-qualifier-function.c
+++ b/clang/test/CodeGen/ptrauth-qualifier-function.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s
-// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s
+// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck --check-prefixes=CHECK,CHECK-CXX %s
#ifdef __cplusplus
extern "C" {
@@ -7,6 +7,8 @@ extern "C" {
void (*fptr)(void);
void (* __ptrauth(0, 0, 42) f2ptr_42_discm)(int);
+void f(int);
+void (* const __ptrauth(0, 0, 42) f_const_ptr)(int) = &f;
// CHECK-LABEL: define void @test_assign_to_qualified
void test_assign_to_qualified() {
@@ -70,6 +72,24 @@ void test_assign_from_qualified() {
// CHECK-NEXT store void ()* [[FPTR10]], void ()** @f2ptr_42_discm
}
+// CHECK-LABEL: define void @test_const_ptr_function_call()
+void test_const_ptr_function_call(void) {
+ f_const_ptr(1);
+
+ // CHECK: call void ptrauth (ptr @f, i32 0, i64 2712)(i32 noundef 1) [ "ptrauth"(i32 0, i64 2712) ]
+}
+
#ifdef __cplusplus
+void (* get_fptr(void))(int);
+void (* __ptrauth(0, 0, 42) f_const_ptr2)(int) = get_fptr();
+void (* const __ptrauth(0, 0, 42) &f_ref)(int) = f_const_ptr2;
+
+// CHECK-CXX-LABEL: define void @test_const_ptr_ref_function_call()
+void test_const_ptr_ref_function_call(void) {
+ f_ref(1);
+
+ // CHECK-CXX: %[[V0:.*]] = load ptr, ptr @f_const_ptr2, align 8
+ // CHECK-CXX: call void %[[V0]](i32 noundef 1) [ "ptrauth"(i32 0, i64 42) ]
+}
}
#endif
More information about the cfe-commits
mailing list