https://github.com/ojhunt updated 
https://github.com/llvm/llvm-project/pull/136828

>From 20b512726682b1ebe81ca6851baa74557dbb1c76 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <[email protected]>
Date: Sat, 13 Dec 2025 15:59:34 -0800
Subject: [PATCH] [clang][PAC] add support for options parameter to __ptrauth

This PR adds support for an 'options' parameter for the __ptrauth
qualifier.
The initial version only exposes the authehntication modes:
 * "strip"
 * "sign-and-strip"
 * "sign-and-auth"

We also support parsing the options but not yet the implementation
 * "isa-pointer"
 * "authenticates-null-values"

The initial support for authentication mode controls exist to support
ABI changes over time, and as a byproduct support basic initial tests
for option parsing.
---
 clang/docs/PointerAuthentication.rst          |  32 +-
 clang/include/clang/Basic/Attr.td             |   6 +-
 .../clang/Basic/DiagnosticParseKinds.td       |   2 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  25 +-
 clang/include/clang/Basic/LangOptions.h       |   7 -
 .../include/clang/Basic/PointerAuthOptions.h  |  49 +++
 clang/include/clang/Parse/Parser.h            |  14 +
 clang/include/clang/Sema/Sema.h               |   6 +-
 clang/lib/AST/TypePrinter.cpp                 |  26 +-
 clang/lib/CodeGen/CGExprConstant.cpp          |  24 +-
 clang/lib/Parse/ParseDecl.cpp                 |  16 +-
 clang/lib/Sema/SemaType.cpp                   | 171 ++++++++-
 clang/test/CodeGen/ptrauth-stripping.c        | 327 ++++++++++++++++++
 clang/test/Parser/ptrauth-qualifier.c         |   2 +-
 clang/test/Sema/ptrauth-qualifier-options.c   | 112 ++++++
 clang/test/Sema/ptrauth-qualifier.c           |  39 ++-
 .../ptrauth-qualifier-constexpr-options.cpp   | 108 ++++++
 17 files changed, 915 insertions(+), 51 deletions(-)
 create mode 100644 clang/test/CodeGen/ptrauth-stripping.c
 create mode 100644 clang/test/Sema/ptrauth-qualifier-options.c
 create mode 100644 clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp

diff --git a/clang/docs/PointerAuthentication.rst 
b/clang/docs/PointerAuthentication.rst
index bf2520b32a3a4..899bca203a137 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -427,7 +427,7 @@ purposes they are all equivalent to ``ptrauth_calls``.
 ``__ptrauth`` qualifier
 ^^^^^^^^^^^^^^^^^^^^^^^
 
-``__ptrauth(key, address, discriminator)`` is an extended type
+``__ptrauth(key, address, discriminator, options)`` is an extended type
 qualifier which causes so-qualified objects to hold pointers or pointer sized
 integers signed using the specified schema rather than the default schema for
 such types.
@@ -452,6 +452,9 @@ The qualifier's operands are as follows:
 
 - ``discriminator`` - a constant discriminator; must be a constant expression
 
+- ``options`` - a constant string expression containing a list of comma
+  separated authentication options; see ``ptrauth_qualifier_options``_
+
 See `Discriminators`_ for more information about discriminators.
 
 Currently the operands must be constant-evaluable even within templates. In the
@@ -463,9 +466,9 @@ qualifiers on a parameter (after parameter type adjustment) 
are ignored when
 deriving the type of the function.  The parameter will be passed using the
 default ABI for the unqualified pointer type.
 
-If ``x`` is an object of type ``__ptrauth(key, address, discriminator) T``,
-then the signing schema of the value stored in ``x`` is a key of ``key`` and
-a discriminator determined as follows:
+If ``x`` is an object of type ``__ptrauth(key, address, discriminator, 
options) T``,
+then the signing schema of the value stored in ``x`` is a key of ``key`` and a
+discriminator determined as follows:
 
 - if ``address`` is 0, then the discriminator is ``discriminator``;
 
@@ -527,6 +530,27 @@ rules of C++:
   indirectly. Thus, changing the address-sensitivity of a type may be
   ABI-breaking even if its size and alignment do not change.
 
+``ptrauth_qualifier_options``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The options parameter to the ``__ptrauth`` qualifier is a string of comma
+separated modifiers to the normal authentication behavior. Currently supported
+options are
+
+- Authentication mode: This is one of ``strip``, ``sign-and-strip``, and
+  ``sign-and-auth``. The ability to modify this behavior is intended to support
+  staging ABI changes. The ``strip`` mode results in the PAC bits of a value
+  being stripped from any value and disabled any other authentication
+  operations. ``sign-and-strip`` strips an authenticated on read, but will
+  ensure a correct signature is set on assignment. Finally ``sign-and-auth`` is
+  the default mode, and provides full protection for the value.
+
+- ``authenticates-null-values``: By default the __ptrauth qualifier does not
+  sign the zero value. This permits fast implementation of null checks in the
+  common case where a null value is safe. The ``authenticates-null-values``
+  option overrides this behavior, and permits null values to be protected with
+  pointer authentication.
+
 ``<ptrauth.h>``
 ~~~~~~~~~~~~~~~
 
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 8ab4aaa6f5781..456070adf27a9 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3765,9 +3765,9 @@ def ObjCRequiresPropertyDefs : InheritableAttr {
 
 def PointerAuth : TypeAttr {
   let Spellings = [CustomKeyword<"__ptrauth">];
-  let Args = [IntArgument<"Key">,
-              BoolArgument<"AddressDiscriminated", 1>,
-              IntArgument<"ExtraDiscriminator", 1>];
+  let Args = [IntArgument<"Key">, BoolArgument<"AddressDiscriminated", 1>,
+              IntArgument<"ExtraDiscriminator", 1>,
+              StringArgument<"Options", 1>];
   let Documentation = [PtrAuthDocs];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index de10dbe5d0628..8f28cfe94f3cc 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1759,7 +1759,7 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
   InGroup<CudaCompat>;
 
 def err_ptrauth_qualifier_bad_arg_count : Error<
-  "'__ptrauth' qualifier must take between 1 and 3 arguments">;
+  "'__ptrauth' qualifier must take between 1 and 4 arguments">;
 
 def warn_cuda_attr_lambda_position : Warning<
   "nvcc does not allow '__%0__' to appear after the parameter list in 
lambdas">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fae63cc0ba139..a7de70b3764a8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1051,6 +1051,21 @@ def err_ptrauth_extra_discriminator_invalid : Error<
   "invalid extra discriminator flag '%0'; '__ptrauth' requires a value between 
"
   "'0' and '%1'">;
 
+// __ptrauth qualifier options string
+def err_ptrauth_dependent_options_string : Error<
+  "'__ptrauth' options argument cannot be a dependent value">;
+def err_ptrauth_options_parse_error : Error<
+  "%select{unexpected comma|unexpected trailing comma|unexpected character 
'%1'|expected a comma before '%1'}0 in '__ptrauth' options argument">;
+def err_ptrauth_unknown_authentication_option: Error<
+  "unknown '__ptrauth' authentication option '%0'">;
+def err_ptrauth_repeated_authentication_option : Error<
+  "repeated '__ptrauth' authentication %select{mode|option}0 '%1'"
+  "%select{, prior mode was '%2'|}0">;
+def note_ptrauth_evaluated_options : Note<
+  "options parameter evaluated to '%0'">;
+def note_ptrauth_previous_authentication_option : Note<
+  "previous option specified here">;
+
 /// main()
 // static main() is not an error in C, just in C++.
 def warn_static_main : Warning<"'main' should not be declared static">,
@@ -1756,15 +1771,15 @@ def note_expr_evaluates_to : Note<
 
 
 def subst_user_defined_msg : TextSubstitution<
-  "%select{the message|the expression}0 in "
-  "%select{a static assertion|this asm operand}0">;
+  "%select{the message|the expression|the expression}0 in "
+  "%select{a static assertion|this asm operand|'__ptrauth' options}0">;
 
 def err_user_defined_msg_invalid : Error<
   "%sub{subst_user_defined_msg}0 must be a string literal or an "
   "object with 'data()' and 'size()' member functions">;
 def err_user_defined_msg_missing_member_function : Error<
-  "the %select{message|string}0 object in "
-  "%select{this static assertion|this asm operand}0 is missing %select{"
+  "the %select{message|string|options}0 object in "
+  "%select{this static assertion|this asm operand|this '__ptrauth' options 
argument}0 is missing %select{"
   "a 'size()' member function|"
   "a 'data()' member function|"
   "'data()' and 'size()' member functions}1">;
@@ -1772,7 +1787,7 @@ def err_user_defined_msg_invalid_mem_fn_ret_ty : Error<
   "%sub{subst_user_defined_msg}0 must have a '%select{size|data}1()' member "
   "function returning an object convertible to '%select{std::size_t|const char 
*}1'">;
 def warn_user_defined_msg_constexpr : Warning<
-  "%select{the message|the expression}0 in "
+  "%select{the message|the expression|the expression}0 in "
   "%select{this static assertion|this asm operand}0 is not a constant 
expression">,
   DefaultError, InGroup<DiagGroup<"invalid-static-assert-message">>;
 def err_user_defined_msg_constexpr : Error<
diff --git a/clang/include/clang/Basic/LangOptions.h 
b/clang/include/clang/Basic/LangOptions.h
index 64ec0a87089f9..f884eb8686729 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -60,13 +60,6 @@ enum class ShaderStage {
   Invalid,
 };
 
-enum class PointerAuthenticationMode : unsigned {
-  None,
-  Strip,
-  SignAndStrip,
-  SignAndAuth
-};
-
 /// Bitfields of LangOptions, split out from LangOptions in order to ensure 
that
 /// this large collection of bitfields is a trivial class type.
 class LangOptionsBase {
diff --git a/clang/include/clang/Basic/PointerAuthOptions.h 
b/clang/include/clang/Basic/PointerAuthOptions.h
index 2b920250721fc..d1e2b9d1abfea 100644
--- a/clang/include/clang/Basic/PointerAuthOptions.h
+++ b/clang/include/clang/Basic/PointerAuthOptions.h
@@ -58,6 +58,55 @@ constexpr unsigned PointerAuthKeyNone = -1;
 /// the vtable type discriminator for classes derived from std::type_info.
 constexpr uint16_t StdTypeInfoVTablePointerConstantDiscrimination = 0xB1EA;
 
+enum class PointerAuthenticationMode : unsigned {
+  None,
+  Strip,
+  SignAndStrip,
+  SignAndAuth
+};
+
+// For reviewers: Where should I actually put this? We don't really have any
+// pre-CG pointer auth specific impl files. I'm almost thinking that we should
+// have Basic/PointerAuth.cpp or something?
+
+static constexpr llvm::StringLiteral PointerAuthenticationOptionStrip = 
"strip";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndStrip =
+    "sign-and-strip";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndAuth =
+    "sign-and-auth";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionIsaPointer =
+    "isa-pointer";
+static constexpr llvm::StringLiteral
+    PointerAuthenticationOptionAuthenticatesNullValues =
+        "authenticates-null-values";
+
+inline std::optional<PointerAuthenticationMode>
+authenticationModeFromString(StringRef Option) {
+  return llvm::StringSwitch<std::optional<PointerAuthenticationMode>>(Option)
+      .Case(PointerAuthenticationOptionStrip, PointerAuthenticationMode::Strip)
+      .Case(PointerAuthenticationOptionSignAndStrip,
+            PointerAuthenticationMode::SignAndStrip)
+      .Case(PointerAuthenticationOptionSignAndAuth,
+            PointerAuthenticationMode::SignAndAuth)
+      .Default(std::nullopt);
+}
+
+inline StringRef stringForAuthenticationMode(PointerAuthenticationMode Mode) {
+  switch (Mode) {
+  case clang::PointerAuthenticationMode::Strip:
+    return PointerAuthenticationOptionStrip;
+  case clang::PointerAuthenticationMode::SignAndStrip:
+    return PointerAuthenticationOptionSignAndStrip;
+  case PointerAuthenticationMode::None:
+    // llvm_unreachable means that this path is assumed unreachable so the
+    // branch can be removed which is much worse that just defaulting to
+    // sign+auth
+    assert(0 && "We should not be stringify disabled pointer auth qualifiers");
+  case clang::PointerAuthenticationMode::SignAndAuth:
+    return PointerAuthenticationOptionSignAndAuth;
+  }
+}
+
 class PointerAuthSchema {
 public:
   enum class Kind : unsigned {
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 5ae02e2b4e8ad..d0eb7e8eefaf5 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -16,6 +16,7 @@
 #include "clang/Basic/OpenACCKinds.h"
 #include "clang/Basic/OperatorPrecedence.h"
 #include "clang/Lex/CodeCompletionHandler.h"
+#include "clang/Lex/LiteralSupport.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaCodeCompletion.h"
@@ -320,6 +321,19 @@ class Parser : public CodeCompletionHandler {
     return PP.LookAhead(N - 1);
   }
 
+  template <class... TokT> bool NextTokenIsStringLiteral(TokT... FollowSet) {
+    static_assert(sizeof...(TokT) > 0, "Follow set cnanot be empty");
+    for (unsigned TokenIdx = 0;; ++TokenIdx) {
+      const Token &LookAheadToken = GetLookAheadToken(TokenIdx);
+      if (LookAheadToken.isOneOf(FollowSet...))
+        return TokenIdx != 0;
+      if (!tokenIsLikeStringLiteral(LookAheadToken, getLangOpts()) ||
+          LookAheadToken.hasUDSuffix())
+        return false;
+    }
+    return true;
+  }
+
   /// NextToken - This peeks ahead one token and returns it without
   /// consuming it.
   const Token &NextToken() { return PP.LookAhead(0); }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 832e46286194a..2aa300e997ca6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6021,7 +6021,11 @@ class Sema final : public SemaBase {
   void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
   void ActOnFinishDelayedMemberInitializers(Decl *Record);
 
-  enum class StringEvaluationContext { StaticAssert = 0, Asm = 1 };
+  enum class StringEvaluationContext {
+    StaticAssert = 0,
+    Asm = 1,
+    PointerAuthOptions = 2
+  };
 
   bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx,
                         StringEvaluationContext EvalContext,
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 3375630827501..64588f5881ccf 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2623,7 +2623,31 @@ void PointerAuthQualifier::print(raw_ostream &OS,
   OS << "__ptrauth(";
   OS << getKey();
   OS << "," << unsigned(isAddressDiscriminated()) << ","
-     << getExtraDiscriminator() << ")";
+     << getExtraDiscriminator();
+
+  llvm::SmallVector<StringRef, 3> Options;
+  PointerAuthenticationMode AuthenticationMode = getAuthenticationMode();
+  assert(AuthenticationMode != PointerAuthenticationMode::None &&
+         "Mode is unauthenticated but claims to be present");
+  // We only include none default authentication modes
+  if (AuthenticationMode != clang::PointerAuthenticationMode::SignAndAuth)
+    Options.push_back(stringForAuthenticationMode(getAuthenticationMode()));
+  if (isIsaPointer())
+    Options.push_back(PointerAuthenticationOptionIsaPointer);
+  if (authenticatesNullValues())
+    Options.push_back(PointerAuthenticationOptionAuthenticatesNullValues);
+  if (Options.size()) {
+    OS << ",";
+    OS << "\"";
+    for (unsigned Idx = 0; Idx < Options.size(); ++Idx) {
+      if (Idx > 0)
+        OS << ",";
+      OS << Options[Idx];
+    }
+    OS << "\"";
+  }
+
+  OS << ")";
 }
 
 std::string Qualifiers::getAsString() const {
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index 0739935acd867..2ce6d9927b511 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2167,6 +2167,13 @@ class ConstantLValueEmitter : public 
ConstStmtVisitor<ConstantLValueEmitter,
 
 }
 
+static bool shouldSignPointer(const PointerAuthQualifier &PointerAuth) {
+  PointerAuthenticationMode AuthenticationMode =
+      PointerAuth.getAuthenticationMode();
+  return AuthenticationMode == PointerAuthenticationMode::SignAndStrip ||
+         AuthenticationMode == PointerAuthenticationMode::SignAndAuth;
+}
+
 llvm::Constant *ConstantLValueEmitter::tryEmit() {
   const APValue::LValueBase &base = Value.getLValueBase();
 
@@ -2200,7 +2207,8 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() {
 
   // Apply pointer-auth signing from the destination type.
   if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth();
-      PointerAuth && !result.HasDestPointerAuth) {
+      PointerAuth && !result.HasDestPointerAuth &&
+      shouldSignPointer(PointerAuth)) {
     value = Emitter.tryEmitConstantSignedPointer(value, PointerAuth);
     if (!value)
       return nullptr;
@@ -2248,8 +2256,9 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
     if (D->hasAttr<WeakRefAttr>())
       return CGM.GetWeakRefReference(D).getPointer();
 
-    auto PtrAuthSign = [&](llvm::Constant *C) {
-      if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth()) {
+    auto PtrAuthSign = [&](llvm::Constant *C, bool IsFunction) {
+      if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth();
+          PointerAuth && shouldSignPointer(PointerAuth)) {
         C = applyOffset(C);
         C = Emitter.tryEmitConstantSignedPointer(C, PointerAuth);
         return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true);
@@ -2257,7 +2266,7 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
 
       CGPointerAuthInfo AuthInfo;
 
-      if (EnablePtrAuthFunctionTypeDiscrimination)
+      if (IsFunction && EnablePtrAuthFunctionTypeDiscrimination)
         AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
 
       if (AuthInfo) {
@@ -2278,18 +2287,19 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
       llvm::Constant *C = CGM.getRawFunctionPointer(FD);
       if (FD->getType()->isCFIUncheckedCalleeFunctionType())
         C = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(C));
-      return PtrAuthSign(C);
+      return PtrAuthSign(C, /*IsFunction=*/true);
     }
 
     if (const auto *VD = dyn_cast<VarDecl>(D)) {
       // We can never refer to a variable with local storage.
       if (!VD->hasLocalStorage()) {
         if (VD->isFileVarDecl() || VD->hasExternalStorage())
-          return CGM.GetAddrOfGlobalVar(VD);
+          return PtrAuthSign(CGM.GetAddrOfGlobalVar(VD), /*IsFunction=*/false);
 
         if (VD->isLocalVarDecl()) {
-          return CGM.getOrCreateStaticVarDecl(
+          llvm::Constant *C = CGM.getOrCreateStaticVarDecl(
               *VD, CGM.getLLVMLinkageVarDefinition(VD));
+          return PtrAuthSign(C, /*IsFunction=*/false);
         }
       }
     }
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 72935f427b7f8..9feaab9fe01fb 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -20,6 +20,7 @@
 #include "clang/Basic/DiagnosticParse.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/LiteralSupport.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Parse/RAIIObjectsForParser.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -3160,18 +3161,25 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes 
&Attrs) {
 
   ArgsVector ArgExprs;
   do {
-    ExprResult ER = ParseAssignmentExpression();
-    if (ER.isInvalid()) {
+    ExprResult ArgumentExpr;
+    if (NextTokenIsStringLiteral(tok::r_paren, tok::comma))
+      ArgumentExpr = ParseUnevaluatedStringLiteralExpression();
+    else {
+      EnterExpressionEvaluationContext ConstantEvaluated(
+          Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+      ArgumentExpr = ParseConstantExpressionInExprEvalContext();
+    }
+    if (ArgumentExpr.isInvalid()) {
       T.skipToEnd();
       return;
     }
-    ArgExprs.push_back(ER.get());
+    ArgExprs.push_back(ArgumentExpr.get());
   } while (TryConsumeToken(tok::comma));
 
   T.consumeClose();
   SourceLocation EndLoc = T.getCloseLocation();
 
-  if (ArgExprs.empty() || ArgExprs.size() > 3) {
+  if (ArgExprs.empty() || ArgExprs.size() > 4) {
     Diag(KwLoc, diag::err_ptrauth_qualifier_bad_arg_count);
     return;
   }
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 2918538ac0f64..d8da14239aeca 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -44,11 +44,13 @@
 #include "clang/Sema/TemplateInstCallback.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <bitset>
 #include <optional>
+#include <span>
 
 using namespace clang;
 
@@ -8631,17 +8633,163 @@ static void HandleNeonVectorTypeAttr(QualType 
&CurType, const ParsedAttr &Attr,
   CurType = S.Context.getVectorType(CurType, numElts, VecKind);
 }
 
+struct PointerAuthQualifierOptions {
+  PointerAuthenticationMode AuthenticationMode =
+      PointerAuthenticationMode::SignAndAuth;
+  bool IsIsaPointer = false;
+  bool AuthenticatesNullValues = false;
+};
+
+static bool
+ParsePointerAuthQualiferOptions(Sema &S, Expr *OptionsExpr,
+                                PointerAuthQualifierOptions &Result) {
+  if (!OptionsExpr)
+    return true;
+
+  if (OptionsExpr->containsErrors())
+    return false;
+
+  if (OptionsExpr->isValueDependent() || OptionsExpr->isTypeDependent()) {
+    S.Diag(OptionsExpr->getExprLoc(),
+           diag::err_ptrauth_dependent_options_string)
+        << OptionsExpr->getSourceRange();
+    return false;
+  }
+
+  std::string EvaluatedOptionsBuffer;
+  StringRef OptionsString;
+  const StringLiteral *OptionsLiteral = dyn_cast<StringLiteral>(OptionsExpr);
+  ASTContext &Ctx = S.getASTContext();
+  if (OptionsLiteral)
+    OptionsString = OptionsLiteral->getString();
+  else if (auto EvaluatedString = OptionsExpr->tryEvaluateString(Ctx)) {
+    EvaluatedString->swap(EvaluatedOptionsBuffer);
+    OptionsString = EvaluatedOptionsBuffer;
+  } else if (!S.EvaluateAsString(
+                 OptionsExpr, EvaluatedOptionsBuffer, Ctx,
+                 Sema::StringEvaluationContext::PointerAuthOptions,
+                 /*ErrorOnInvalidMessage=*/true))
+    OptionsString = EvaluatedOptionsBuffer;
+  else
+    return false;
+
+  auto Failed = [&](SourceRange Range = {}, unsigned DiagId = 0,
+                    auto... DiagArgs) {
+    if (Range.isValid())
+      (S.Diag(Range.getBegin(), DiagId) << ... << DiagArgs) << Range;
+    if (!OptionsLiteral)
+      S.Diag(OptionsExpr->getExprLoc(), diag::note_ptrauth_evaluated_options)
+          << EvaluatedOptionsBuffer << OptionsExpr->getSourceRange();
+    return false;
+  };
+
+  SmallVector<StringRef, 4> Options;
+  auto ParseString = OptionsString.trim();
+  if (ParseString.empty())
+    return true;
+
+  auto FindDiagnosticRange = [&](auto Token) {
+    if (!OptionsLiteral)
+      return OptionsExpr->getSourceRange();
+    unsigned StartOffset = Token.begin() - OptionsString.begin();
+    unsigned EndOffset = StartOffset + Token.size();
+    SourceLocation StartLoc =
+        S.getLocationOfStringLiteralByte(OptionsLiteral, StartOffset);
+    SourceLocation EndLoc =
+        S.getLocationOfStringLiteralByte(OptionsLiteral, EndOffset);
+    return SourceRange(StartLoc, EndLoc);
+  };
+
+  // Split up the options
+  auto IsOptionCharacter = [](char Ch) {
+    return llvm::isAlpha(Ch) || Ch == '-';
+  };
+  while (!ParseString.empty()) {
+    if (!Options.empty()) {
+      if (ParseString.size() <= 1 || !ParseString.consume_front(','))
+        break;
+      ParseString = ParseString.ltrim();
+    }
+    StringRef Option = ParseString.take_while(IsOptionCharacter);
+    if (Option.empty())
+      break;
+    Options.push_back(Option);
+    ParseString = ParseString.drop_front(Option.size()).ltrim();
+  }
+
+  if (!ParseString.empty()) {
+    StringRef LastOption;
+    if (!Options.empty())
+      LastOption = Options.back();
+    if (StringRef UnexpectedOption = ParseString.take_while(IsOptionCharacter);
+        !UnexpectedOption.empty()) {
+      SourceRange DiagRange = FindDiagnosticRange(UnexpectedOption);
+      return Failed(DiagRange, diag::err_ptrauth_options_parse_error,
+                    /*Expected Comma*/ 3, UnexpectedOption);
+    }
+    unsigned DiagIdx = 2; // unexpected character
+    if (ParseString.starts_with(','))
+      DiagIdx = ParseString.size() == 1;
+    StringRef ErrorToken = ParseString.take_front();
+    SourceRange DiagRange = FindDiagnosticRange(ErrorToken);
+    return Failed(DiagRange, diag::err_ptrauth_options_parse_error, DiagIdx,
+                  ErrorToken, LastOption);
+  }
+
+  StringRef AuthenticationModeOption;
+  StringRef IsIsaPointerOption;
+  StringRef AuthenticatesNullValuesOption;
+  for (StringRef CurrentOption : Options) {
+    StringRef *Storage = nullptr;
+    SourceRange DiagRange = FindDiagnosticRange(CurrentOption);
+    if (authenticationModeFromString(CurrentOption))
+      Storage = &AuthenticationModeOption;
+    else if (CurrentOption == PointerAuthenticationOptionIsaPointer)
+      Storage = &IsIsaPointerOption;
+    else if (CurrentOption ==
+             PointerAuthenticationOptionAuthenticatesNullValues)
+      Storage = &AuthenticatesNullValuesOption;
+    else
+      return Failed(DiagRange, diag::err_ptrauth_unknown_authentication_option,
+                    CurrentOption);
+
+    if (Storage->empty()) {
+      *Storage = CurrentOption;
+      continue;
+    }
+    bool NotAuthenticationMode = !authenticationModeFromString(CurrentOption);
+    Failed(DiagRange.getBegin(),
+           diag::err_ptrauth_repeated_authentication_option,
+           NotAuthenticationMode, CurrentOption, *Storage);
+    if (OptionsLiteral) {
+      SourceRange PreviousRange = FindDiagnosticRange(*Storage);
+      S.Diag(PreviousRange.getBegin(),
+             diag::note_ptrauth_previous_authentication_option)
+          << PreviousRange;
+    }
+    return false;
+  }
+
+  Result.AuthenticationMode =
+      authenticationModeFromString(AuthenticationModeOption)
+          .value_or(PointerAuthenticationMode::SignAndAuth);
+  Result.IsIsaPointer = !IsIsaPointerOption.empty();
+  Result.AuthenticatesNullValues = !AuthenticatesNullValuesOption.empty();
+  return true;
+}
+
 /// Handle the __ptrauth qualifier.
 static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
                                    const ParsedAttr &Attr, Sema &S) {
-
-  assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 3) &&
-         "__ptrauth qualifier takes between 1 and 3 arguments");
+  assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 4) &&
+         "__ptrauth qualifier takes between 1 and 4 arguments");
   Expr *KeyArg = Attr.getArgAsExpr(0);
   Expr *IsAddressDiscriminatedArg =
       Attr.getNumArgs() >= 2 ? Attr.getArgAsExpr(1) : nullptr;
   Expr *ExtraDiscriminatorArg =
       Attr.getNumArgs() >= 3 ? Attr.getArgAsExpr(2) : nullptr;
+  Expr *AuthenticationOptionsArg =
+      Attr.getNumArgs() >= 4 ? Attr.getArgAsExpr(3) : nullptr;
 
   unsigned Key;
   if (S.checkConstantPointerAuthKey(KeyArg, Key)) {
@@ -8657,11 +8805,9 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, 
QualType &T,
                                                    IsAddressDiscriminated);
   IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
       ExtraDiscriminatorArg, PointerAuthDiscArgKind::Extra, 
ExtraDiscriminator);
-
-  if (IsInvalid) {
-    Attr.setInvalid();
-    return;
-  }
+  PointerAuthQualifierOptions Options;
+  IsInvalid |=
+      !ParsePointerAuthQualiferOptions(S, AuthenticationOptionsArg, Options);
 
   if (!T->isSignableType(Ctx) && !T->isDependentType()) {
     S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_invalid_target) << T;
@@ -8681,12 +8827,17 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, 
QualType &T,
     return;
   }
 
+  if (IsInvalid) {
+    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, /*IsIsaPointer=*/false,
-      /*AuthenticatesNullValues=*/false);
+      Options.AuthenticationMode, Options.IsIsaPointer,
+      Options.AuthenticatesNullValues);
   T = S.Context.getPointerAuthType(T, Qual);
 }
 
diff --git a/clang/test/CodeGen/ptrauth-stripping.c 
b/clang/test/CodeGen/ptrauth-stripping.c
new file mode 100644
index 0000000000000..4e187d8debdc7
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-stripping.c
@@ -0,0 +1,327 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls 
-fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s 
+
+typedef void *NonePointer;
+typedef void *__ptrauth(1, 1, 101, "strip") StripPointer;
+typedef void *__ptrauth(1, 1, 102, "sign-and-strip") SignAndStripPointer;
+typedef void *__ptrauth(1, 1, 103, "sign-and-auth") SignAndAuthPointer;
+typedef __UINT64_TYPE__ NoneIntptr;
+typedef __UINT64_TYPE__ __ptrauth(1, 0, 105, "strip") StripIntptr;
+typedef __UINT64_TYPE__ __ptrauth(1, 0, 106, "sign-and-strip") 
SignAndStripIntptr;
+typedef __UINT64_TYPE__ __ptrauth(1, 0, 107, "sign-and-auth") 
SignAndAuthIntptr;
+
+NonePointer globalNonePointer = "foo0";
+StripPointer globalStripPointer = "foo1";
+SignAndStripPointer globalSignAndStripPointer = "foo2";
+SignAndAuthPointer globalSignAndAuthPointer = "foo3";
+NoneIntptr globalNoneIntptr = (__UINT64_TYPE__)&globalNonePointer;
+StripIntptr globalStripIntptr = (__UINT64_TYPE__)&globalStripPointer;
+SignAndStripIntptr globalSignAndStripIntptr = 
(__UINT64_TYPE__)&globalSignAndStripPointer;
+SignAndAuthIntptr globalSignAndAuthIntptr = 
(__UINT64_TYPE__)&globalSignAndAuthPointer;
+
+// CHECK: @.str = private unnamed_addr constant [5 x i8] c"foo0\00", align 1
+// CHECK: @globalNonePointer = global ptr @.str, align 8
+// CHECK: @.str.1 = private unnamed_addr constant [5 x i8] c"foo1\00", align 1
+// CHECK: @globalStripPointer = global ptr @.str.1, align 8
+// CHECK: @.str.2 = private unnamed_addr constant [5 x i8] c"foo2\00", align 1
+// CHECK: @globalSignAndStripPointer = global ptr ptrauth (ptr @.str.2, i32 1, 
i64 102, ptr @globalSignAndStripPointer), align 8
+// CHECK: @.str.3 = private unnamed_addr constant [5 x i8] c"foo3\00", align 1
+// CHECK: @globalSignAndAuthPointer = global ptr ptrauth (ptr @.str.3, i32 1, 
i64 103, ptr @globalSignAndAuthPointer), align 8
+// CHECK: @globalNoneIntptr = global i64 ptrtoint (ptr @globalNonePointer to 
i64), align 8
+// CHECK: @globalStripIntptr = global i64 ptrtoint (ptr @globalStripPointer to 
i64), align 8
+// CHECK: @globalSignAndStripIntptr = global i64 ptrtoint (ptr ptrauth (ptr 
@globalSignAndStripPointer, i32 1, i64 106) to i64), align 8
+// CHECK: @globalSignAndAuthIntptr = global i64 ptrtoint (ptr ptrauth (ptr 
@globalSignAndAuthPointer, i32 1, i64 107) to i64), align 8
+
+typedef struct {
+  NonePointer ptr;
+  NoneIntptr i;
+} NoneStruct;
+typedef struct {
+  StripPointer ptr;
+  StripIntptr i;
+} StripStruct;
+typedef struct {
+  SignAndStripPointer ptr;
+  SignAndStripIntptr i;
+} SignAndStripStruct;
+typedef struct {
+  SignAndAuthPointer ptr;
+  SignAndAuthIntptr i;
+} SignAndAuthStruct;
+
+// CHECK-LABEL: @testNone
+NoneStruct testNone(NoneStruct *a, NoneStruct *b, NoneStruct c) {
+  globalNonePointer += 1;
+  // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalNonePointer
+  // CHECK: [[GLOBALPP:%.*]] = getelementptr inbounds i8, ptr [[GLOBALP]], i64 
1
+  // CHECK: store ptr [[GLOBALPP]], ptr @globalNonePointer
+  globalNoneIntptr += 1;
+  // CHECK: [[GLOBALI:%.*]] = load i64, ptr @globalNoneIntptr
+  // CHECK: [[GLOBALIP:%.*]] = add i64 [[GLOBALI]], 1
+  // CHECK: store i64 [[GLOBALIP]], ptr @globalNoneIntptr
+  a->ptr += 1;
+  // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8
+  // CHECK: [[PTR_PTR:%.*]] = getelementptr inbounds nuw %struct.NoneStruct, 
ptr [[PTR]], i32 0, i32 0
+  // CHECK: [[PTR:%.*]] = load ptr, ptr [[PTR_PTR]], align 8
+  // CHECK: [[AP:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 1
+  // CHECK: store ptr [[AP]], ptr [[PTR_PTR]], align 8
+  a->i += 1;
+  // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8
+  // CHECK: [[I_PTR:%.*]] = getelementptr inbounds nuw %struct.NoneStruct, ptr 
[[PTR]], i32 0, i32 1
+  // CHECK: [[I:%.*]] = load i64, ptr [[I_PTR]], align 8
+  // CHECK: [[IP:%.*]] = add i64 [[I]], 1
+  // CHECK: store i64 [[IP]], ptr [[I_PTR]], align 8
+  *b = *a;
+  // CHECK: [[B_ADDR:%.*]] = load ptr, ptr %b.addr, align 8
+  // CHECK: [[A_ADDR:%.*]] = load ptr, ptr %a.addr, align 8
+  // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[B_ADDR]], ptr align 
8 [[A_ADDR]], i64 16, i1 false)
+  return c;
+}
+
+// CHECK-LABEL: @testStrip1
+void testStrip1() {
+  globalStripPointer += 1;
+  // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalStripPointer
+  // CHECK: [[GLOBALPI:%.*]] = ptrtoint ptr [[GLOBALP]] to i64
+  // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[GLOBALPI]], i32 1)
+}
+// CHECK-LABEL: @testStrip2
+void testStrip2(StripStruct *a) {
+  a->ptr += 1;
+  // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+  // CHECK: [[PTR:%.*]] = getelementptr inbounds nuw %struct.StripStruct, ptr 
[[A]], i32 0, i32 0
+  // CHECK: [[APTR:%.*]] = load ptr, ptr [[PTR]]
+  // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64
+  // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 1)
+}
+// CHECK-LABEL: @testStrip3
+void testStrip3(StripStruct *a) {
+  a->i += 1;
+  // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+  // CHECK: [[I:%.*]] = getelementptr inbounds nuw %struct.StripStruct, ptr 
[[A]], i32 0, i32 1
+  // CHECK: [[I64:%.*]] = load i64, ptr [[I]]
+  // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1)
+}
+// CHECK-LABEL: @testStrip4
+void testStrip4(StripStruct *a, StripStruct *b) {
+  *b = *a;
+  // CHECK: call void @__copy_assignment_8_8_pa1_101_0_t8w8(ptr %0, ptr %1)
+}
+
+// CHECK-LABEL: @testStrip5
+StripStruct testStrip5(StripStruct a) {
+  return a;
+  // CHECK: call void @__copy_constructor_8_8_pa1_101_0_t8w8(ptr %agg.result, 
ptr %a)
+}
+
+// CHECK-LABEL: @testSignAndStrip1
+void testSignAndStrip1(void) {
+  globalSignAndStripPointer += 1;
+  // CHECK: [[GP:%.*]] = load ptr, ptr @globalSignAndStripPointer
+  // CHECK: [[GPI:%.*]] = ptrtoint ptr [[GP]] to i64
+  // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[GPI]], i32 1)
+  // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr
+  // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], 
%resign.nonnull ]
+  // CHECK: [[ADDPTR:%.*]] = getelementptr inbounds i8, ptr [[PHI]], i64 1
+  // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr 
@globalSignAndStripPointer to i64), i64 102)
+  // CHECK: [[ADDPTRI:%.*]] = ptrtoint ptr [[ADDPTR]] to i64
+  // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 [[ADDPTRI]], i32 1, i64 
[[DISC]])
+}
+
+// CHECK-LABEL: @testSignAndStrip2
+void testSignAndStrip2(SignAndStripStruct *a) {
+  a->ptr += 1;
+  // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+  // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr 
[[A]], i32 0, i32 0
+  // CHECK: [[APTR:%.*]] = load ptr, ptr %ptr
+  // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64
+  // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 
1)
+  // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr
+  // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], 
%resign.nonnull ]
+  // CHECK: %add.ptr = getelementptr inbounds i8, ptr [[PHI]], i64 1
+  // CHECK: [[PTRI:%.*]] = ptrtoint ptr %ptr to i64
+  // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[PTRI]], i64 102)
+  // CHECK: [[APTRI:%.*]] = ptrtoint ptr %add.ptr to i64
+  // CHECK: call i64 @llvm.ptrauth.sign(i64 [[APTRI]], i32 1, i64 [[DISC]])
+}
+
+// CHECK-LABEL: @testSignAndStrip3
+void testSignAndStrip3(SignAndStripStruct *a) {
+  a->i += 1;
+  // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+  // CHECK: [[I:%.*]] = getelementptr inbounds nuw %struct.SignAndStripStruct, 
ptr [[A]], i32 0, i32 1
+  // CHECK: [[I64:%.*]] = load i64, ptr [[I]]
+  // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1)
+  // CHECK: [[PHI:%.*]] = phi i64 [ 0, %entry ], [ [[STRIPPED]], 
%resign.nonnull ]
+  // CHECK: %add = add i64 [[PHI]], 1
+  // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 106)
+}
+
+// CHECK-LABEL: @testSignAndStrip4
+void testSignAndStrip4(SignAndStripStruct *a, SignAndStripStruct *b) {
+  *b = *a;
+  // CHECK: call void @__copy_assignment_8_8_pa1_102_0_t8w8(ptr %0, ptr %1)
+}
+
+// CHECK-LABEL: @testSignAndStrip5
+SignAndStripStruct testSignAndStrip5(SignAndStripStruct a) {
+  return a;
+  // CHECK: call void @__copy_constructor_8_8_pa1_102_0_t8w8(ptr %agg.result, 
ptr %a)
+}
+
+// CHECK-LABEL: @testSignAndAuth1
+void testSignAndAuth1() {
+  globalSignAndAuthPointer += 1;
+  // CHECK: %0 = load ptr, ptr @globalSignAndAuthPointer
+  // CHECK: %1 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr 
@globalSignAndAuthPointer to i64), i64 103)
+  // CHECK: %3 = ptrtoint ptr %0 to i64
+  // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 1, i64 %1)
+  // CHECK: %5 = inttoptr i64 %4 to ptr
+  // CHECK: %6 = phi ptr [ null, %entry ], [ %5, %resign.nonnull ]
+  // CHECK: %add.ptr = getelementptr inbounds i8, ptr %6, i64 1
+  // CHECK: %7 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr 
@globalSignAndAuthPointer to i64), i64 103)
+  // CHECK: %8 = ptrtoint ptr %add.ptr to i64
+  // CHECK: %9 = call i64 @llvm.ptrauth.sign(i64 %8, i32 1, i64 %7)
+}
+
+// CHECK-LABEL: @testSignAndAuth2
+void testSignAndAuth2(SignAndAuthStruct *a) {
+  a->i += 1;
+  // CHECK: %0 = load ptr, ptr %a.addr
+  // CHECK: %i = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, 
i32 0, i32 1
+  // CHECK: %1 = load i64, ptr %i
+  // CHECK: %3 = call i64 @llvm.ptrauth.auth(i64 %1, i32 1, i64 107)
+  // CHECK: %4 = phi i64 [ 0, %entry ], [ %3, %resign.nonnull ]
+  // CHECK: %add = add i64 %4, 1
+  // CHECK: %6 = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 107)
+  // CHECK: %7 = phi i64 [ 0, %resign.cont ], [ %6, %resign.nonnull1 ]
+}
+
+// CHECK-LABEL: @testSignAndAuth3
+void testSignAndAuth3(SignAndAuthStruct *a) {
+  a->ptr += 1;
+  // CHECK: %0 = load ptr, ptr %a.addr
+  // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr 
%0, i32 0, i32 0
+  // CHECK: %1 = load ptr, ptr %ptr
+  // CHECK: %2 = ptrtoint ptr %ptr to i64
+  // CHECK: %3 = call i64 @llvm.ptrauth.blend(i64 %2, i64 103)
+  // CHECK: %5 = ptrtoint ptr %1 to i64
+  // CHECK: %6 = call i64 @llvm.ptrauth.auth(i64 %5, i32 1, i64 %3)
+  // CHECK: %7 = inttoptr i64 %6 to ptr
+  // CHECK: %8 = phi ptr [ null, %entry ], [ %7, %resign.nonnull ]
+  // CHECK: %add.ptr = getelementptr inbounds i8, ptr %8, i64 1
+  // CHECK: %9 = ptrtoint ptr %ptr to i64
+  // CHECK: %10 = call i64 @llvm.ptrauth.blend(i64 %9, i64 103)
+  // CHECK: %11 = ptrtoint ptr %add.ptr to i64
+  // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %10)
+}
+
+// CHECK-LABEL: @testSignAndAuth4
+void testSignAndAuth4(SignAndAuthStruct *a, SignAndAuthStruct *b) {
+  *b = *a;
+  // CHECK: call void @__copy_assignment_8_8_pa1_103_0_t8w8(ptr %0, ptr %1)
+}
+
+// CHECK-LABEL: @testSignAndAuth5
+SignAndAuthStruct testSignAndAuth5(SignAndAuthStruct a) {
+  return a;
+  // CHECK: call void @__copy_constructor_8_8_pa1_103_0_t8w8(ptr %agg.result, 
ptr %a)
+}
+
+// CHECK-LABEL: @testCoercions1
+void testCoercions1(StripStruct *a, SignAndStripStruct *b) {
+  a->ptr = b->ptr;
+  // CHECK: %0 = load ptr, ptr %a.addr
+  // CHECK: %ptr = getelementptr inbounds nuw %struct.StripStruct, ptr %0, i32 
0, i32 0
+  // CHECK: %1 = load ptr, ptr %b.addr
+  // CHECK: %ptr1 = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr 
%1, i32 0, i32 0
+  // CHECK: %2 = load ptr, ptr %ptr1
+  // CHECK: %3 = ptrtoint ptr %ptr1 to i64
+  // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 102)
+  // CHECK: %8 = ptrtoint ptr %2 to i64
+  // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1)
+}
+
+// CHECK-LABEL: @testCoercions2
+void testCoercions2(StripStruct *a, SignAndAuthStruct *b) {
+  b->ptr = a->ptr;
+  // CHECK: store ptr %a, ptr %a.addr
+  // CHECK: store ptr %b, ptr %b.addr
+  // CHECK: %0 = load ptr, ptr %b.addr
+  // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr 
%0, i32 0, i32 0
+  // CHECK: %1 = load ptr, ptr %a.addr
+  // CHECK: %ptr1 = getelementptr inbounds nuw %struct.StripStruct, ptr %1, 
i32 0, i32 0
+  // CHECK: %2 = load ptr, ptr %ptr1
+  // CHECK: %3 = ptrtoint ptr %ptr1 to i64
+  // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 101)
+  // CHECK: %5 = ptrtoint ptr %ptr to i64
+  // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 103)
+  // CHECK: %7 = icmp ne ptr %2, null
+  // CHECK: %8 = ptrtoint ptr %2 to i64
+  // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1)
+  // CHECK: %10 = inttoptr i64 %9 to ptr
+  // CHECK: %11 = ptrtoint ptr %10 to i64
+  // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6)
+}
+
+// CHECK-LABEL: @testCoercions3
+void testCoercions3(SignAndStripStruct *a, SignAndAuthStruct *b) {
+  a->ptr = b->ptr;
+  // CHECK: %0 = load ptr, ptr %a.addr
+  // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr 
%0, i32 0, i32 0
+  // CHECK: %1 = load ptr, ptr %b.addr
+  // CHECK: %ptr1 = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr 
%1, i32 0, i32 0
+  // CHECK: %2 = load ptr, ptr %ptr1
+  // CHECK: %3 = ptrtoint ptr %ptr1 to i64
+  // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 103)
+  // CHECK: %5 = ptrtoint ptr %ptr to i64
+  // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 102)
+  // CHECK: %8 = ptrtoint ptr %2 to i64
+  // CHECK: %9 = call i64 @llvm.ptrauth.auth(i64 %8, i32 1, i64 %4)
+  // CHECK: %10 = inttoptr i64 %9 to ptr
+  // CHECK: %11 = ptrtoint ptr %10 to i64
+  // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6)
+  // CHECK: %13 = inttoptr i64 %12 to ptr
+  // CHECK: %14 = phi ptr [ null, %entry ], [ %13, %resign.nonnull ]
+}
+
+// CHECK-LABEL: @testCoercions4
+void testCoercions4(StripStruct *a, SignAndStripStruct *b) {
+  a->i = b->i;
+  // CHECK: store ptr %a, ptr %a.addr
+  // CHECK: store ptr %b, ptr %b.addr
+  // CHECK: %0 = load ptr, ptr %a.addr
+  // CHECK: %i = getelementptr inbounds nuw %struct.StripStruct, ptr %0, i32 
0, i32 1
+  // CHECK: %1 = load ptr, ptr %b.addr
+  // CHECK: %i1 = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr 
%1, i32 0, i32 1
+  // CHECK: %2 = load i64, ptr %i1
+  // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1)
+  // CHECK: %5 = phi i64 [ 0, %entry ], [ %4, %resign.nonnull ]
+}
+
+// CHECK-LABEL: @testCoercions5
+void testCoercions5(StripStruct *a, SignAndAuthStruct *b) {
+  b->i = a->i;
+  // CHECK: %0 = load ptr, ptr %b.addr
+  // CHECK: %i = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, 
i32 0, i32 1
+  // CHECK: %1 = load ptr, ptr %a.addr
+  // CHECK: %i1 = getelementptr inbounds nuw %struct.StripStruct, ptr %1, i32 
0, i32 1
+  // CHECK: %2 = load i64, ptr %i1
+  // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1)
+  // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 107)
+  // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ]
+  // CHECK: store i64 %6, ptr %i
+}
+
+// CHECK-LABEL: @testCoercions6
+void testCoercions6(SignAndStripStruct *a, SignAndAuthStruct *b) {
+  a->i = b->i;
+  // CHECK: %0 = load ptr, ptr %a.addr
+  // CHECK: %i = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr 
%0, i32 0, i32 1
+  // CHECK: %1 = load ptr, ptr %b.addr
+  // CHECK: %i1 = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr 
%1, i32 0, i32 1
+  // CHECK: %2 = load i64, ptr %i1
+  // CHECK: %3 = icmp ne i64 %2, 0
+  // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %2, i32 1, i64 107)
+  // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 106)
+  // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ]
+}
diff --git a/clang/test/Parser/ptrauth-qualifier.c 
b/clang/test/Parser/ptrauth-qualifier.c
index 2071ac6c2d661..014613e0a3bbd 100644
--- a/clang/test/Parser/ptrauth-qualifier.c
+++ b/clang/test/Parser/ptrauth-qualifier.c
@@ -15,4 +15,4 @@ int nonConstantGlobal = 5;
 
 __ptrauth int invalid0; // expected-error{{expected '('}}
 __ptrauth() int invalid1; // expected-error{{expected expression}}
-int * __ptrauth(VALID_DATA_KEY, 1, 1000, 12) invalid12; // 
expected-error{{qualifier must take between 1 and 3 arguments}}
+int * __ptrauth(VALID_DATA_KEY, 1, 1000, 12, 24) invalid12; // 
expected-error{{qualifier must take between 1 and 4 arguments}}
diff --git a/clang/test/Sema/ptrauth-qualifier-options.c 
b/clang/test/Sema/ptrauth-qualifier-options.c
new file mode 100644
index 0000000000000..19bfc0f4c46b8
--- /dev/null
+++ b/clang/test/Sema/ptrauth-qualifier-options.c
@@ -0,0 +1,112 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c23 -fsyntax-only -verify 
-fptrauth-intrinsics %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c23 -fsyntax-only -verify 
-fptrauth-intrinsics %s
+
+#ifndef __PTRAUTH__
+#error "the ptrauth qualifier should be available"
+#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
+
+
+typedef int *intp;
+
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "Foo") invalid13_1;
+// expected-error@-1 {{unknown '__ptrauth' authentication option 'Foo'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14_1;
+// expected-error@-1 {{'__ptrauth' qualifier must take between 1 and 4 
arguments}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15;
+// expected-error@-1 {{repeated '__ptrauth' authentication mode 
'sign-and-strip', prior mode was 'strip'}}
+// expected-note@-2 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid16;
+// expected-error@-1 {{repeated '__ptrauth' authentication option}}
+// expected-note@-2 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip, , isa-pointer") invalid18;
+// expected-error@-1 {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19;
+// expected-error@-1 {{unexpected trailing comma in '__ptrauth' options 
argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20;
+// expected-error@-1 {{unexpected trailing comma in '__ptrauth' options 
argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21;
+// expected-error@-1 {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22;
+// expected-error@-1 {{expected a comma before 'isa-pointer' in '__ptrauth' 
options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23;
+// expected-error@-1 {{expected a comma before 'isa-pointer' in '__ptrauth' 
options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip"
+                                         " isa-pointer") invalid24;
+// expected-error@-1 {{expected a comma before 'isa-pointer' in '__ptrauth' 
options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") 
invalid25;
+// expected-error@-1 {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") 
invalid26;
+// expected-error@-1 {{unexpected comma in '__ptrauth' options argument}}
+
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip") valid12;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip") valid13;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth") valid14;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "isa-pointer") valid15;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth,isa-pointer") valid15;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,isa-pointer") valid16;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer") valid17;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip,isa-pointer") valid18;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ,isa-pointer") valid19;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip, isa-pointer") valid20;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer ") valid21;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip") valid22;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ") valid23;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip ") valid24;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,"
+                                     "isa-pointer") valid25;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip"
+                                     ",isa-pointer") valid26;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\n,isa-pointer") valid27;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\t,isa-pointer") valid28;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "") valid29;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "authenticates-null-values") valid30;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "isa-pointer, authenticates-null-values") 
valid31;
+
+int *global_ptr;
+
+// Check qualifier option printing
+int *__ptrauth(1, 1, 0, "strip") * strip = &global_ptr;
+// expected-error@-1 {{int *__ptrauth(1,1,0,"strip") *}}
+int *__ptrauth(1, 1, 0, "sign-and-strip") * signAndStrip = &global_ptr;
+// expected-error@-1 {{int *__ptrauth(1,1,0,"sign-and-strip") *}}
+int *__ptrauth(1, 1, 0, "sign-and-auth") * signAndAuth = &global_ptr;
+// expected-error@-1 {{int *__ptrauth(1,1,0) *}}
+int *__ptrauth(1, 1, 0, "sign-and-auth, isa-pointer") * signAndAuthIsa = 
&global_ptr;
+// expected-error@-1 {{int *__ptrauth(1,1,0,"isa-pointer") *}}
+int *__ptrauth(1, 1, 0, "sign-and-strip, isa-pointer") * signAndStripIsa = 
&global_ptr;
+// expected-error@-1 {{int *__ptrauth(1,1,0,"sign-and-strip,isa-pointer") *'}}
+int *__ptrauth(1, 1, 0, "sign-and-strip, authenticates-null-values") * 
signAndAuthAuthenticatesNullValues = &global_ptr;
+// expected-error@-1 {{int 
*__ptrauth(1,1,0,"sign-and-strip,authenticates-null-values") *}}
+int *__ptrauth(1, 1, 0, "sign-and-strip, authenticates-null-values, 
isa-pointer") * signAndAuthIsaPointerAuthenticatesNullValues = &global_ptr;
+// expected-error@-1 {{int 
*__ptrauth(1,1,0,"sign-and-strip,isa-pointer,authenticates-null-values") *}}
+
+// Check mismatching options are identified
+void f() {
+    // default auth matches explicit use of sign-and-auth
+    int *__ptrauth(1, 1, 0) * signAndAuthDefault = signAndAuth;
+
+    int *__ptrauth(1, 1, 0, "sign-and-strip") * signAndStrip2 = strip;
+    // expected-error@-1 {{initializing 'int 
*__ptrauth(1,1,0,"sign-and-strip") *' with an expression of type 'int 
*__ptrauth(1,1,0,"strip") *' changes pointer authentication of pointee type}}
+    int *__ptrauth(1, 1, 0) * signAndAuth2 = signAndStrip;
+    // expected-error@-1 {{'int *__ptrauth(1,1,0) *' with an expression of 
type 'int *__ptrauth(1,1,0,"sign-and-strip") *' changes pointer authentication 
of pointee type}}
+    int *__ptrauth(1, 1, 0, "authenticates-null-values") * 
signAndAuthAuthenticatesNullValues2 = signAndAuth;
+    // expected-error@-1 {{initializing 'int 
*__ptrauth(1,1,0,"authenticates-null-values") *' with an expression of type 
'int *__ptrauth(1,1,0) *' changes pointer authentication of pointee type}}
+    int *__ptrauth(0, 1, 0) * signAndAuth3 = signAndAuth; // different key
+    // expected-error@-1 {{initializing 'int *__ptrauth(0,1,0) *' with an 
expression of type 'int *__ptrauth(1,1,0) *' changes pointer authentication of 
pointee type}}
+    int *__ptrauth(1, 0, 0) * signAndAuth4 = signAndAuth; // different address 
discrimination
+    // expected-error@-1 {{initializing 'int *__ptrauth(1,0,0) *' with an 
expression of type 'int *__ptrauth(1,1,0) *' changes pointer authentication of 
pointee type}}
+    int *__ptrauth(1, 1, 1) * signAndAuth5 = signAndAuth; // different 
discriminator
+    // expected-error@-1 {{initializing 'int *__ptrauth(1,1,1) *' with an 
expression of type 'int *__ptrauth(1,1,0) *' changes pointer authentication of 
pointee type}}
+}
+
+
+
+
diff --git a/clang/test/Sema/ptrauth-qualifier.c 
b/clang/test/Sema/ptrauth-qualifier.c
index 3e568ce9f37e3..94cf1a587c00c 100644
--- a/clang/test/Sema/ptrauth-qualifier.c
+++ b/clang/test/Sema/ptrauth-qualifier.c
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -triple arm64-apple-ios -DIS_DARWIN -std=c23 -fsyntax-only 
-verify -fptrauth-intrinsics %s
 // RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c23 -fsyntax-only -verify 
-fptrauth-intrinsics %s
 
+// #include <ptrauth.h>
+
 #if defined(IS_DARWIN) && !__has_extension(ptrauth_qualifier)
 // This error means that the __ptrauth qualifier availability test says  that 
it
 // is not available. This error is not expected in the output, if it is seen
@@ -46,19 +48,42 @@ int * __ptrauth(VALID_DATA_KEY, 1, -1) invalid9; // 
expected-error {{invalid ext
 int * __ptrauth(VALID_DATA_KEY, 1, 100000) invalid10; // expected-error 
{{invalid extra discriminator flag '100000'; '__ptrauth' requires a value 
between '0' and '65535'}}
 int * __ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // 
expected-error {{argument to '__ptrauth' must be an integer constant 
expression}}
 int * __ptrauth(VALID_DATA_KEY, nonConstantGlobal, 1000) invalid13; // 
expected-error {{argument to '__ptrauth' must be an integer constant 
expression}}
-int * __ptrauth(nonConstantGlobal, 1, 1000) invalid14; // 
expected-error{{expression is not an integer constant expression}}
-
+int * __ptrauth(nonConstantGlobal, 1, 1000) invalid14; // expected-error 
{{expression is not an integer constant expression}}
+
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, 41) invalid11;          // 
expected-error {{the expression in '__ptrauth' options must be a string literal 
or an object with 'data()' and 'size()' member functions}}
+int *__ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12;  // 
expected-error {{argument to '__ptrauth' must be an integer constant 
expression}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "Foo") invalid13;       // 
expected-error {{unknown '__ptrauth' authentication option 'Foo'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14; // 
expected-error {{'__ptrauth' qualifier must take between 1 and 4 arguments}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15;    
 // expected-error {{repeated '__ptrauth' authentication mode 'sign-and-strip', 
prior mode was 'strip'}}
+                                                                               
 // expected-note@-1 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19;                  
 // expected-error {{unexpected trailing comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20;                       
 // expected-error {{unexpected trailing comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21;                      
 // expected-error {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid22; 
 // expected-error {{repeated '__ptrauth' authentication option 'isa-pointer'}}
+                                                                               
 // expected-note@-1 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid23;       
// expected-error {{expected a comma before 'isa-pointer' in '__ptrauth' 
options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip"                               
 
+                                         " isa-pointer") invalid24;            
 // expected-error {{expected a comma before 'isa-pointer' in '__ptrauth' 
options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,,isa-pointer") invalid25; 
// expected-error {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,isa-pointer,") invalid26; 
// expected-error {{unexpected trailing comma in '__ptrauth' options argument}}
 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;
+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;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip") valid12;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip") valid13;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth") valid14;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip") valid22;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ") valid23;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip ") valid24;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "") valid29;
 
 int * __ptrauth(VALID_DATA_KEY) array0[10];
 int (* __ptrauth(VALID_DATA_KEY) array1)[10];
diff --git a/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp 
b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp
new file mode 100644
index 0000000000000..2bba455f46941
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++2b -Wno-string-plus-int 
-fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s
+
+struct S {
+  static constexpr auto options = "strip";
+};
+
+struct string_view {
+  int S;
+  const char* D;
+  constexpr string_view() : S(0), D(0){}
+  constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
+  constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
+  constexpr int size() const {
+      return S;
+  }
+  constexpr const char* data() const {
+      return D;
+  }
+};
+template <class StringType> constexpr const char* const_options(int i) {
+  const char* local_const = "isa-pointer";
+  constexpr auto local_constexpr = ",";
+  static constexpr auto static_const = "strip";
+  static constexpr auto static_constexpr = "sign-and-strip";
+
+  switch (i) {
+    case 0:
+      return "";
+    case 1:
+      return "authenticates-null-values";
+    case 2:
+      return local_const;
+    case 3:
+      return local_constexpr;
+    case 4:
+      return static_const;
+    case 5:
+      return static_constexpr;
+    case 6:
+      return "some characters";
+    case 7:
+      return S::options;
+    case 8:
+      return const_options<StringType>(3)+1;
+    default:
+      #ifdef __EXCEPTIONS
+      throw "invalid index";
+      #else
+      __builtin_trap();
+      #endif
+  }
+}
+
+// When we support dependent pointer auth qualifiers this can become a template
+// function rather than manually duplicating it.
+void test_func_charptr() {
+  using StringType = const char*;
+  int * __ptrauth(1,1,1,const_options<StringType>(0)) zero;
+  int * __ptrauth(1,1,1,const_options<StringType>(1)) one;
+  int * __ptrauth(1,1,1,const_options<StringType>(2)) two;
+  int * __ptrauth(1,1,1,const_options<StringType>(3)) three;
+  // expected-error@-1 {{unexpected trailing comma in '__ptrauth' options 
argument}}
+  // expected-note@-2 {{options parameter evaluated to ','}}
+  int * __ptrauth(1,1,1,const_options<StringType>(4)) four;
+  int * __ptrauth(1,1,1,const_options<StringType>(5)) five;
+  int * __ptrauth(1,1,1,const_options<StringType>(6)) six;
+  // expected-error@-1 {{expected a comma before 'characters' in '__ptrauth' 
options argument}}
+  // expected-note@-2 {{options parameter evaluated to 'some characters'}}
+  int * __ptrauth(1,1,1,const_options<StringType>(7)) seven;
+  int * __ptrauth(1,1,1,const_options<StringType>(8)) eight;
+  int * __ptrauth(1,1,1,2 * 3) ice;
+  // expected-error@-1 {{the expression in '__ptrauth' options must be a 
string literal or an object with 'data()' and 'size()' member functions}}
+  int * __ptrauth(1,1,1,4 + "wat,strip") arithmetic_string;
+  int * __ptrauth(1,1,1,5 + "wat,strip") arithmetic_string2;
+  // expected-error@-1 {{unknown '__ptrauth' authentication option 'trip'}}
+  // expected-note@-2 {{options parameter evaluated to 'trip'}}
+
+  // Handle evaluation failing
+  int * __ptrauth(1,1,1,const_options<StringType>(50)) fifty;
+  // expected-error@-1 {{the expression in '__ptrauth' options must be a 
string literal or an object with 'data()' and 'size()' member functions}}
+}
+
+void test_func_string_view() {
+  using StringType = string_view;
+  int * __ptrauth(1,1,1,const_options<StringType>(0)) zero;
+  int * __ptrauth(1,1,1,const_options<StringType>(1)) one;
+  int * __ptrauth(1,1,1,const_options<StringType>(2)) two;
+  int * __ptrauth(1,1,1,const_options<StringType>(3)) three;
+  // expected-error@-1 {{unexpected trailing comma in '__ptrauth' options 
argument}}
+  // expected-note@-2 {{options parameter evaluated to ','}}
+  int * __ptrauth(1,1,1,const_options<StringType>(4)) four;
+  int * __ptrauth(1,1,1,const_options<StringType>(5)) five;
+  int * __ptrauth(1,1,1,const_options<StringType>(6)) six;
+  // expected-error@-1 {{expected a comma before 'characters' in '__ptrauth' 
options argument}}
+  // expected-note@-2 {{options parameter evaluated to 'some characters'}}
+  int * __ptrauth(1,1,1,const_options<StringType>(7)) seven;
+  int * __ptrauth(1,1,1,const_options<StringType>(8)) eight;
+  int * __ptrauth(1,1,1,2 * 3) ice;
+  // expected-error@-1 {{the expression in '__ptrauth' options must be a 
string literal or an object with 'data()' and 'size()' member functions}}
+  int * __ptrauth(1,1,1,4 + "wat,strip") arithmetic_string;
+  int * __ptrauth(1,1,1,5 + "wat,strip") arithmetic_string2;
+  // expected-error@-1 {{unknown '__ptrauth' authentication option 'trip'}}
+  // expected-note@-2 {{options parameter evaluated to 'trip'}}
+
+  // Handle evaluation failing
+  int * __ptrauth(1,1,1,const_options<StringType>(50)) fifty;
+  // expected-error@-1 {{the expression in '__ptrauth' options must be a 
string literal or an object with 'data()' and 'size()' member functions}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to