[clang] [PAC] Add support for __ptrauth type qualifier (PR #100830)

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 26 17:15:23 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/3] [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/3] 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/3] 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(),



More information about the cfe-commits mailing list