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

>From d32a1bd141a668a2d8e8c6315a38506c7e1be4c7 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oli...@apple.com>
Date: Wed, 30 Apr 2025 22:26:59 -0700
Subject: [PATCH] [clang][ptrauth] 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"

There are other options that will be added in future, but for now
these are the modes already representable by PointerAuthQualifier.

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/include/clang/Basic/Attr.td             |   6 +-
 .../clang/Basic/DiagnosticParseKinds.td       |   4 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  25 +-
 clang/include/clang/Basic/LangOptions.h       |   6 +
 clang/lib/CodeGen/CGExprConstant.cpp          |  24 +-
 clang/lib/Parse/ParseDecl.cpp                 |   2 +-
 clang/lib/Sema/SemaType.cpp                   | 153 +++++++++++-
 clang/test/CodeGen/ptrauth-stripping.c        | 234 ++++++++++++++++++
 clang/test/Parser/ptrauth-qualifier.c         |   2 +-
 clang/test/Sema/ptrauth-qualifier-options.c   |  65 +++++
 clang/test/Sema/ptrauth-qualifier.c           |  29 ++-
 .../ptrauth-qualifier-constexpr-options.cpp   |  65 +++++
 12 files changed, 587 insertions(+), 28 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/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index a734eb6658c3d..ca943739fb53c 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3579,9 +3579,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 72e765bcb800d..859e391258692 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1721,8 +1721,8 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
   "argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
   InGroup<CudaCompat>;
 
-def err_ptrauth_qualifier_bad_arg_count : Error<
-  "'__ptrauth' qualifier must take between 1 and 3 arguments">;
+def err_ptrauth_qualifier_bad_arg_count
+    : Error<"'__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 823d4bb687497..e2fdeb730241d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1040,6 +1040,23 @@ def err_ptrauth_address_discrimination_invalid : Error<
 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 note_ptrauth_evaluating_options
+    : Note<"options parameter evaluated to '%0'">;
+def err_ptrauth_invalid_option : Error<"'%0' options parameter %1">;
+def err_ptrauth_unknown_authentication_option
+    : Error<"unknown '%0' authentication option '%1'">;
+def err_ptrauth_repeated_authentication_option
+    : Error<"repeated '%0' authentication %select{mode|option}1%select{, prior 
mode was '%3'| '%2'}1">;
+def note_ptrauth_previous_authentication_option
+    : Note<"previous '%0' authentication %select{mode|option}1">;
+def err_ptrauth_unexpected_option_end
+    : Error<"unexpected end of options parameter for %0">;
+def err_ptrauth_option_unexpected_token
+    : Error<"unexpected character '%0' in '%1' options">;
+def err_ptrauth_option_missing_comma
+    : Error<"missing comma after '%1' option in '%0' qualifier">;
+
 /// main()
 // static main() is not an error in C, just in C++.
 def warn_static_main : Warning<"'main' should not be declared static">,
@@ -1730,10 +1747,10 @@ def err_static_assert_requirement_failed : Error<
 def note_expr_evaluates_to : Note<
   "expression evaluates to '%0 %1 %2'">;
 
-
-def subst_user_defined_msg : TextSubstitution<
-  "%select{the message|the expression}0 in "
-  "%select{a static assertion|this asm operand}0">;
+def subst_user_defined_msg
+    : TextSubstitution<"%select{the message|the expression|the expression}0 in 
"
+                       "%select{a static assertion|this asm operand|a pointer "
+                       "authentication option}0">;
 
 def err_user_defined_msg_invalid : Error<
   "%sub{subst_user_defined_msg}0 must be a string literal or an "
diff --git a/clang/include/clang/Basic/LangOptions.h 
b/clang/include/clang/Basic/LangOptions.h
index 40debd961f752..49039e923f24f 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -65,6 +65,12 @@ enum class PointerAuthenticationMode : unsigned {
   SignAndAuth
 };
 
+static constexpr llvm::StringLiteral PointerAuthenticationOptionStrip = 
"strip";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndStrip =
+    "sign-and-strip";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndAuth =
+    "sign-and-auth";
+
 /// 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/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index b21ebeee4bed1..84e2df1a55788 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2129,6 +2129,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();
 
@@ -2162,7 +2169,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;
@@ -2210,8 +2218,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);
@@ -2219,7 +2228,7 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
 
       CGPointerAuthInfo AuthInfo;
 
-      if (EnablePtrAuthFunctionTypeDiscrimination)
+      if (IsFunction && EnablePtrAuthFunctionTypeDiscrimination)
         AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
 
       if (AuthInfo) {
@@ -2237,17 +2246,18 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
     };
 
     if (const auto *FD = dyn_cast<FunctionDecl>(D))
-      return PtrAuthSign(CGM.getRawFunctionPointer(FD));
+      return PtrAuthSign(CGM.getRawFunctionPointer(FD), /*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 0747790b96d2f..d8486f3f70886 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3428,7 +3428,7 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes 
&Attrs) {
   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 6e7ee8b5506ff..4c7350ddbd399 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -48,6 +48,7 @@
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <bitset>
+#include <cctype>
 #include <optional>
 
 using namespace clang;
@@ -8350,14 +8351,16 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, 
const ParsedAttr &Attr,
 /// 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");
+  StringRef AttrName = Attr.getAttrName()->getName();
   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)) {
@@ -8373,21 +8376,151 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, 
QualType &T,
                                                    IsAddressDiscriminated);
   IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
       ExtraDiscriminatorArg, Sema::PADAK_ExtraDiscPtrAuth, ExtraDiscriminator);
+  std::string LastAuthenticationMode;
+  std::optional<PointerAuthenticationMode> AuthenticationMode = std::nullopt;
+  bool IsIsaPointer = false;
+  bool AuthenticatesNullValues = false;
+
+  if (AuthenticationOptionsArg && !AuthenticationOptionsArg->containsErrors()) 
{
+    StringRef OptionsString;
+    std::string EvaluatedString;
+    bool HasEvaluatedOptionsString = false;
+    const StringLiteral *OptionsStringLiteral =
+        dyn_cast<StringLiteral>(AuthenticationOptionsArg);
+    SourceRange AuthenticationOptionsRange =
+        AuthenticationOptionsArg->getSourceRange();
+    bool ReportedEvaluation = false;
+    auto ReportEvaluationOfExpressionIfNeeded = [&]() {
+      if (OptionsStringLiteral || !HasEvaluatedOptionsString ||
+          ReportedEvaluation)
+        return;
+      ReportedEvaluation = true;
+      S.Diag(AuthenticationOptionsRange.getBegin(),
+             diag::note_ptrauth_evaluating_options)
+          << OptionsString << AuthenticationOptionsRange;
+    };
+    auto DiagnoseInvalidOptionsParameter = [&](llvm::StringRef Reason) {
+      S.Diag(AuthenticationOptionsRange.getBegin(),
+             diag::err_ptrauth_invalid_option)
+          << AttrName << Reason;
+      Attr.setInvalid();
+      ReportEvaluationOfExpressionIfNeeded();
+    };
+    if (AuthenticationOptionsArg->isValueDependent() ||
+        AuthenticationOptionsArg->isTypeDependent()) {
+      DiagnoseInvalidOptionsParameter("is dependent");
+      return;
+    }
+    if (OptionsStringLiteral) {
+      OptionsString = OptionsStringLiteral->getString();
+      HasEvaluatedOptionsString = true;
+    } else {
+      Expr::EvalResult Eval;
+      bool Result = AuthenticationOptionsArg->EvaluateAsRValue(Eval, Ctx);
+      if (Result && Eval.Val.isLValue()) {
+        auto *BaseExpr = Eval.Val.getLValueBase().dyn_cast<const Expr *>();
+        const StringLiteral *EvaluatedStringLiteral =
+            dyn_cast<StringLiteral>(const_cast<Expr *>(BaseExpr));
+        if (EvaluatedStringLiteral) {
+          CharUnits StartOffset = Eval.Val.getLValueOffset();
+          EvaluatedString = EvaluatedStringLiteral->getString().drop_front(
+              StartOffset.getQuantity());
+          OptionsString = EvaluatedString;
+          HasEvaluatedOptionsString = true;
+        }
+      }
+    }
+    if (!HasEvaluatedOptionsString) {
+      DiagnoseInvalidOptionsParameter(
+          "must be a string of comma separated flags");
+      return;
+    }
+    for (char Ch : OptionsString) {
+      if (Ch != '-' && Ch != ',' && !isWhitespace(Ch) && !isalpha(Ch)) {
+        DiagnoseInvalidOptionsParameter("contains invalid characters");
+        return;
+      }
+    }
+    HasEvaluatedOptionsString = true;
+    OptionsString = OptionsString.trim();
+    llvm::SmallVector<StringRef> Options;
+    if (!OptionsString.empty())
+      OptionsString.split(Options, ',');
+
+    auto OptionHandler = [&](auto Value, auto *Option,
+                             std::string *LastOption = nullptr) {
+      return [&, Value, Option, LastOption](StringRef OptionString) {
+        if (!*Option) {
+          *Option = Value;
+          if (LastOption)
+            *LastOption = OptionString;
+          return true;
+        }
+        bool IsAuthenticationMode =
+            std::is_same_v<decltype(Value), PointerAuthenticationMode>;
+        S.Diag(AuthenticationOptionsRange.getBegin(),
+               diag::err_ptrauth_repeated_authentication_option)
+            << AttrName << !IsAuthenticationMode << OptionString
+            << (LastOption ? *LastOption : "");
+        return false;
+      };
+    };
 
-  if (IsInvalid) {
-    Attr.setInvalid();
-    return;
+    for (unsigned Idx = 0; Idx < Options.size(); ++Idx) {
+      StringRef Option = Options[Idx].trim();
+      if (Option.empty()) {
+        bool IsLastOption = Idx == (Options.size() - 1);
+        DiagnoseInvalidOptionsParameter(
+            IsLastOption ? "has a trailing comma" : "contains an empty 
option");
+        continue;
+      }
+      auto SelectedHandler =
+          llvm::StringSwitch<std::function<bool(StringRef)>>(Option)
+              .Case(PointerAuthenticationOptionStrip,
+                    OptionHandler(PointerAuthenticationMode::Strip,
+                                  &AuthenticationMode, 
&LastAuthenticationMode))
+              .Case(PointerAuthenticationOptionSignAndStrip,
+                    OptionHandler(PointerAuthenticationMode::SignAndStrip,
+                                  &AuthenticationMode, 
&LastAuthenticationMode))
+              .Case(PointerAuthenticationOptionSignAndAuth,
+                    OptionHandler(PointerAuthenticationMode::SignAndAuth,
+                                  &AuthenticationMode, 
&LastAuthenticationMode))
+              .Default([&](StringRef Option) {
+                if (size_t WhitespaceIndex =
+                        Option.find_first_of(" \t\n\v\f\r");
+                    WhitespaceIndex != Option.npos) {
+                  StringRef LeadingOption = Option.slice(0, WhitespaceIndex);
+                  S.Diag(AuthenticationOptionsRange.getBegin(),
+                         diag::err_ptrauth_option_missing_comma)
+                      << AttrName << LeadingOption;
+                } else {
+                  S.Diag(AuthenticationOptionsRange.getBegin(),
+                         diag::err_ptrauth_unknown_authentication_option)
+                      << AttrName << Option;
+                }
+                return false;
+              });
+      if (!SelectedHandler(Option))
+        IsInvalid = true;
+    }
   }
 
   if (!T->isSignableType() && !T->isDependentType()) {
     S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << T;
+    IsInvalid = true;
+  }
+
+  if (IsInvalid) {
     Attr.setInvalid();
     return;
   }
 
+  if (!AuthenticationMode)
+    AuthenticationMode = PointerAuthenticationMode::SignAndAuth;
+
   if (T.getPointerAuth()) {
     S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_redundant)
-        << T << Attr.getAttrName()->getName();
+        << T << AttrName;
     Attr.setInvalid();
     return;
   }
@@ -8401,9 +8534,11 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, 
QualType &T,
   assert((!IsAddressDiscriminatedArg || IsAddressDiscriminated <= 1) &&
          "address discriminator arg should be either 0 or 1");
   PointerAuthQualifier Qual = PointerAuthQualifier::Create(
-      Key, IsAddressDiscriminated, ExtraDiscriminator,
-      PointerAuthenticationMode::SignAndAuth, false, false);
+      Key, IsAddressDiscriminated, ExtraDiscriminator, *AuthenticationMode,
+      IsIsaPointer, AuthenticatesNullValues);
+  assert(Qual.getAuthenticationMode() == *AuthenticationMode);
   T = S.Context.getPointerAuthType(T, Qual);
+  assert(T.getPointerAuth().getAuthenticationMode() == *AuthenticationMode);
 }
 
 /// HandleArmSveVectorBitsTypeAttr - The "arm_sve_vector_bits" attribute is
diff --git a/clang/test/CodeGen/ptrauth-stripping.c 
b/clang/test/CodeGen/ptrauth-stripping.c
new file mode 100644
index 0000000000000..dce07030a902e
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-stripping.c
@@ -0,0 +1,234 @@
+// 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;
+
+NonePointer globalNonePointer = "foo0";
+StripPointer globalStripPointer = "foo1";
+SignAndStripPointer globalSignAndStripPointer = "foo2";
+SignAndAuthPointer globalSignAndAuthPointer = "foo3";
+NoneIntptr globalNoneIntptr = (__UINT64_TYPE__)&globalNonePointer;
+
+// 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
+
+typedef struct {
+  NonePointer ptr;
+  NoneIntptr i;
+} NoneStruct;
+typedef struct {
+  StripPointer ptr;
+} StripStruct;
+typedef struct {
+  SignAndStripPointer ptr;
+} SignAndStripStruct;
+typedef struct {
+  SignAndAuthPointer ptr;
+} 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: [[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: @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: @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: @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 ]
+}
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..b079b264ce16a
--- /dev/null
+++ b/clang/test/Sema/ptrauth-qualifier-options.c
@@ -0,0 +1,65 @@
+// 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
+
+_Static_assert(__has_extension(ptrauth_qualifier), "the ptrauth qualifier 
should be available");
+
+#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, prior mode 
was 'strip'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid16;
+// expected-error@-1 {{repeated '__ptrauth' authentication option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip, , isa-pointer") invalid18;
+// expected-error@-1 {{'__ptrauth' options parameter contains an empty option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19;
+// expected-error@-1 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20;
+// expected-error@-1 {{'__ptrauth' options parameter contains an empty option}}
+// expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21;
+// expected-error@-1 2 {{'__ptrauth' options parameter contains an empty 
option}}
+// expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22;
+// expected-error@-1 {{missing comma after 'strip' option in '__ptrauth' 
qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23;
+// expected-error@-1 {{missing comma after 'strip' option in '__ptrauth' 
qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip"
+                                         " isa-pointer") invalid24;
+// expected-error@-2{{missing comma after 'strip' option in '__ptrauth' 
qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") 
invalid25; // expected-error{{'__ptrauth' options parameter contains an empty 
option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") 
invalid26; // expected-error{{'__ptrauth' options parameter contains an empty 
option}}
+
+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;
diff --git a/clang/test/Sema/ptrauth-qualifier.c 
b/clang/test/Sema/ptrauth-qualifier.c
index 2ec4471e519ca..7c2c6503b3639 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 -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 !__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
@@ -34,7 +36,25 @@ 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 {{'__ptrauth' options parameter must be a string of comma 
separated flags}}
+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, prior mode was 
'strip'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19;                  
 // expected-error {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20;                       
 // expected-error {{'__ptrauth' options parameter contains an empty option}}
+                                                                               
 // expected-error@-1 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21;                      
 // expected-error {{'__ptrauth' options parameter contains an empty option}}
+                                                                               
 // expected-error@-1 {{'__ptrauth' options parameter contains an empty option}}
+                                                                               
 // expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22;       
 // expected-error {{missing comma after 'strip' option in '__ptrauth' 
qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23;      
 // expected-error {{missing comma after 'strip' option in '__ptrauth' 
qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip"                               
 // expected-error {{missing comma after 'strip' option in '__ptrauth' 
qualifier}}
+                                         " isa-pointer") invalid24;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") 
invalid25; // expected-error {{'__ptrauth' options parameter contains an empty 
option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") 
invalid26; // expected-error {{'__ptrauth' options parameter contains an empty 
option}}
 
 int * __ptrauth(VALID_DATA_KEY) valid0;
 int * __ptrauth(VALID_DATA_KEY) *valid1;
@@ -47,6 +67,13 @@ 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..941cf1c57e1ba
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp
@@ -0,0 +1,65 @@
+// 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";
+};
+
+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(3)+1;
+    default:
+      #ifdef __EXCEPTIONS
+      throw "invalid index";
+      #else
+      __builtin_trap();
+      #endif
+  }
+}
+
+void test_func() {
+  int * __ptrauth(1,1,1,const_options(0)) zero;
+  int * __ptrauth(1,1,1,const_options(1)) one;
+  int * __ptrauth(1,1,1,const_options(2)) two;
+  int * __ptrauth(1,1,1,const_options(3)) three;
+  // expected-error@-1 {{'__ptrauth' options parameter contains an empty 
option}}
+  // expected-error@-2 {{'__ptrauth' options parameter has a trailing comma}}
+  // expected-note@-3 {{options parameter evaluated to ','}}
+  int * __ptrauth(1,1,1,const_options(4)) four;
+  int * __ptrauth(1,1,1,const_options(5)) five;
+  int * __ptrauth(1,1,1,const_options(6)) six;
+  // expected-error@-1 {{missing comma after 'some' option in '__ptrauth' 
qualifier}}
+  int * __ptrauth(1,1,1,const_options(7)) seven;
+  int * __ptrauth(1,1,1,const_options(8)) eight;
+  int * __ptrauth(1,1,1,S::options) struct_access;
+  int * __ptrauth(1,1,1,2 * 3) ice;
+  // expected-error@-1 {{'__ptrauth' options parameter must be a string of 
comma separated flags}}
+  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'}}
+
+  // Handle evaluation failing
+  int * __ptrauth(1,1,1,const_options(50)) fifty;
+  // expected-error@-1 {{'__ptrauth' options parameter must be a string of 
comma separated flags}}
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to