https://github.com/jroelofs updated 
https://github.com/llvm/llvm-project/pull/202742

>From 638e90e5ecbe3c96071b8431a51ab1f712177461 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Tue, 9 Jun 2026 12:01:23 -0700
Subject: [PATCH 1/9] [arm64e] Add a builtin + intrinsic for arm64e PAuth_LR:
 __builtin_ptrauth_auth_with_pc_and_resign

The new builtin behaves like __builtin_ptrauth_auth_and_resign, but incorporates
the address of the signing instruction (i.e. the `pacibsppc`/`paciasppc`) when
performing the auth side, and subsequently re-signs using a different scheme.
Authenticating the re-signed value will fail if and only if authenticating the
original value with the incorporated pc would have failed.
---
 clang/docs/PointerAuthentication.rst          | 16 ++++
 clang/include/clang/Basic/Builtins.td         |  6 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +-
 clang/lib/CodeGen/CGBuiltin.cpp               | 13 +++
 clang/lib/Headers/ptrauth.h                   | 36 +++++++
 clang/lib/Sema/SemaChecking.cpp               | 41 +++++++-
 clang/test/CodeGen/ptrauth-intrinsics.c       | 14 +++
 clang/test/Sema/ptrauth-intrinsics-macro.c    |  5 +
 clang/test/Sema/ptrauth.c                     | 27 ++++++
 llvm/include/llvm/IR/Intrinsics.td            | 13 +++
 llvm/lib/IR/Verifier.cpp                      |  8 ++
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    | 44 +++++++++
 .../GISel/AArch64InstructionSelector.cpp      | 61 ++++++++++++
 ...rauth-intrinsic-auth-with-pc-and-resign.ll | 93 +++++++++++++++++++
 14 files changed, 378 insertions(+), 3 deletions(-)
 create mode 100644 
llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll

diff --git a/clang/docs/PointerAuthentication.rst 
b/clang/docs/PointerAuthentication.rst
index bf2520b32a3a4..9f0a30caf5343 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -687,6 +687,22 @@ computations may still be attackable.  In the future, 
Clang should be enhanced
 to guarantee non-attackability if these expressions are
 :ref:`safely-derived<Safe derivation>`.
 
+``ptrauth_auth_with_pc_and_resign``
+
+.. code-block:: c
+
+  ptrauth_auth_with_pc_and_resign(pointer, oldKey, oldDiscriminator, oldPC, 
newKey, newDiscriminator)
+
+Similar to :ref:`ptrauth_auth_and_resign`, but additionally requires that the
+signature includes the address of the signing instruction (i.e. uses 
`paciasppc`
+/ `pacibsppc` instead of `paciasp` / `pacibsp`). This authenticates ``pointer``
+signed with ``oldKey`` and ``oldDiscriminator`` at ``oldPC``, then resigns the
+raw-pointer result with ``newKey`` and ``newDiscriminator``.
+
+Note: ``oldKey`` must be ``ptrauth_key_asia`` (IA) or ``ptrauth_key_asib`` 
(IB),
+as only these keys support PC-based authentication instructions. Data keys
+(``ptrauth_key_asda`` / ``ptrauth_key_asdb``) are not supported.
+
 ``ptrauth_auth_function``
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index e7fb43be8794e..d8a3215cd8627 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5013,6 +5013,12 @@ def PtrauthAuthAndResign : Builtin {
   let Prototype = "void*(void*,int,void*,int,void*)";
 }
 
+def PtrauthAuthWithPCAndResign : Builtin {
+  let Spellings = ["__builtin_ptrauth_auth_with_pc_and_resign"];
+  let Attributes = [CustomTypeChecking, NoThrow];
+  let Prototype = "void*(void*,int,void*,void*,int,void*)";
+}
+
 def PtrauthAuthLoadRelativeAndSign : Builtin {
   let Spellings = ["__builtin_ptrauth_auth_load_relative_and_sign"];
   let Attributes = [CustomTypeChecking, NoThrow];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d3e2d616a8b80..224726d35f294 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1010,7 +1010,7 @@ def err_ptrauth_invalid_key :
         "the current target">;
 def err_ptrauth_value_bad_type :
   Error<"%select{signed value|extra discriminator|blended pointer|blended "
-        "integer}0 must have %select{pointer|integer|pointer or integer}1 "
+        "integer|blended pc}0 must have %select{pointer|integer|pointer or 
integer}1 "
         "type; type here is %2">;
 def err_ptrauth_bad_constant_pointer :
   Error<"argument to ptrauth_sign_constant must refer to a global variable "
@@ -1025,6 +1025,8 @@ def warn_ptrauth_sign_null_pointer :
 def warn_ptrauth_auth_null_pointer :
   Warning<"authenticating a null pointer will almost certainly trap">,
   InGroup<PtrAuthNullPointers>;
+def err_ptrauth_auth_with_pc_and_resign_invalid_key :
+  Error<"ptrauth_auth_with_pc_and_resign only supports auth with IA and IB 
keys, not %0">;
 def err_ptrauth_string_not_literal : Error<
   "argument must be a string literal%select{| of char type}0">;
 def err_ptrauth_type_disc_undiscriminated : Error<
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 682b125890fe1..84103f3d4f6b3 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6249,6 +6249,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl 
GD, unsigned BuiltinID,
 
   case Builtin::BI__builtin_ptrauth_auth:
   case Builtin::BI__builtin_ptrauth_auth_and_resign:
+  case Builtin::BI__builtin_ptrauth_auth_with_pc_and_resign:
   case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
   case Builtin::BI__builtin_ptrauth_blend_discriminator:
   case Builtin::BI__builtin_ptrauth_sign_generic_data:
@@ -6265,6 +6266,16 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl 
GD, unsigned BuiltinID,
       Args[0] = Builder.CreatePtrToInt(Args[0], IntPtrTy);
 
     switch (BuiltinID) {
+    case Builtin::BI__builtin_ptrauth_auth_with_pc_and_resign:
+      // Convert oldDiscriminator (arg 2), oldPC (arg 3) and newDiscriminator 
(arg 5) to intptr_t
+      if (Args[2]->getType()->isPointerTy())
+        Args[2] = Builder.CreatePtrToInt(Args[2], IntPtrTy);
+      if (Args[3]->getType()->isPointerTy())
+        Args[3] = Builder.CreatePtrToInt(Args[3], IntPtrTy);
+      if (Args[5]->getType()->isPointerTy())
+        Args[5] = Builder.CreatePtrToInt(Args[5], IntPtrTy);
+      break;
+
     case Builtin::BI__builtin_ptrauth_auth_and_resign:
     case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
       if (Args[4]->getType()->isPointerTy())
@@ -6294,6 +6305,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl 
GD, unsigned BuiltinID,
         return Intrinsic::ptrauth_auth;
       case Builtin::BI__builtin_ptrauth_auth_and_resign:
         return Intrinsic::ptrauth_resign;
+      case Builtin::BI__builtin_ptrauth_auth_with_pc_and_resign:
+        return Intrinsic::ptrauth_auth_with_pc_and_resign;
       case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
         return Intrinsic::ptrauth_resign_load_relative;
       case Builtin::BI__builtin_ptrauth_blend_discriminator:
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index cde1b3c4ebbe2..2b9201d1c8a2b 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -178,6 +178,31 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
   __builtin_ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, 
\
                                     __new_data)
 
+/* Authenticate a pointer using a PC-based signature scheme and resign
+   it using a different scheme.
+
+   If the result is subsequently authenticated using the new scheme, that
+   authentication is guaranteed to fail if and only if the initial
+   authentication failed.
+
+   The value must be an expression of pointer type.
+   The key must be a constant expression of type ptrauth_key.
+   The extra data must be an expression of pointer or integer type;
+   if an integer, it will be coerced to ptrauth_extra_data_t.
+   The oldpc must be an expression of pointer or integer type representing
+   the PC value where the original signature was created.
+   The result will have the same type as the original value.
+
+   This operation is guaranteed to not leave the intermediate value
+   available for attack before it is re-signed. The authentication is
+   performed using autia171615/autib171615 instructions which include the
+   PC value in the signature.
+
+   Do not pass a null pointer to this function. A null pointer
+   will not successfully authenticate. */
+#define ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data, 
__old_pc, __new_key, __new_data) \
+  __builtin_ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data, 
__old_pc, __new_key, __new_data)
+
 /* Authenticate a pointer using one scheme, load 32bit value at offset addend
    from the pointer, and add this value to the pointer, sign using specified
    scheme.
@@ -396,6 +421,17 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
     __value;                                                                   
\
   })
 
+#define ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data, 
__old_pc, \
+                                        __new_key, __new_data)                 
\
+  ({                                                                           
\
+    (void)__old_key;                                                           
\
+    (void)__old_data;                                                          
\
+    (void)__old_pc;                                                            
\
+    (void)__new_key;                                                           
\
+    (void)__new_data;                                                          
\
+    __value;                                                                   
\
+  })
+
 #define ptrauth_auth_load_relative_and_sign(__value, __old_key, __old_data,    
\
                                             __new_key, __new_data, __offset)   
\
   __extension__({                                                              
\
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b8a3f48a32f24..1d64f666d347e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1550,7 +1550,8 @@ enum PointerAuthOpKind {
   PAO_SignGeneric,
   PAO_Discriminator,
   PAO_BlendPointer,
-  PAO_BlendInteger
+  PAO_BlendInteger,
+  PAO_BlendPC
 };
 }
 
@@ -1676,7 +1677,7 @@ static bool checkPointerAuthValue(Sema &S, Expr *&Arg, 
PointerAuthOpKind OpKind,
   };
   auto AllowsInteger = [](PointerAuthOpKind OpKind) {
     return OpKind == PAO_Discriminator || OpKind == PAO_BlendInteger ||
-           OpKind == PAO_SignGeneric;
+           OpKind == PAO_SignGeneric || OpKind == PAO_BlendPC;
   };
 
   // Require the value to have the right range of type.
@@ -1695,6 +1696,7 @@ static bool checkPointerAuthValue(Sema &S, Expr *&Arg, 
PointerAuthOpKind OpKind,
         << unsigned(OpKind == PAO_Discriminator  ? 1
                     : OpKind == PAO_BlendPointer ? 2
                     : OpKind == PAO_BlendInteger ? 3
+                    : OpKind == PAO_BlendPC      ? 4
                                                  : 0)
         << unsigned(AllowsInteger(OpKind) ? (AllowsPointer(OpKind) ? 2 : 1) : 
0)
         << Arg->getType() << Arg->getSourceRange();
@@ -1861,6 +1863,39 @@ static ExprResult PointerAuthAuthAndResign(Sema &S, 
CallExpr *Call) {
   return Call;
 }
 
+static ExprResult PointerAuthAuthWithPCAndResign(Sema &S, CallExpr *Call) {
+  if (S.checkArgCount(Call, 6))
+    return ExprError();
+  if (checkPointerAuthEnabled(S, Call))
+    return ExprError();
+  if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) ||
+      checkPointerAuthKey(S, Call->getArgs()[1]) ||
+      checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) ||
+      checkPointerAuthValue(S, Call->getArgs()[3], PAO_BlendPC) ||
+      checkPointerAuthKey(S, Call->getArgs()[4]) ||
+      checkPointerAuthValue(S, Call->getArgs()[5], PAO_Discriminator))
+    return ExprError();
+
+  // Validate that the oldKey is IA or IB, not DA or DB.
+  // This enforces the constraint that auth_with_pc_and_resign only supports
+  // IA/IB keys for authentication, as only those keys support the PC-based
+  // signing instructions (paciasppc/pacibsppc).
+  unsigned OldKey = 0;
+  if (!S.checkConstantPointerAuthKey(Call->getArgs()[1], OldKey)) {
+    using AK = PointerAuthSchema::ARM8_3Key;
+    if (OldKey != static_cast<unsigned>(AK::ASIA) &&
+        OldKey != static_cast<unsigned>(AK::ASIB)) {
+      S.Diag(Call->getArgs()[1]->getExprLoc(),
+             diag::err_ptrauth_auth_with_pc_and_resign_invalid_key)
+          << OldKey << Call->getArgs()[1]->getSourceRange();
+      return ExprError();
+    }
+  }
+
+  Call->setType(Call->getArgs()[0]->getType());
+  return Call;
+}
+
 static ExprResult PointerAuthAuthLoadRelativeAndSign(Sema &S, CallExpr *Call) {
   if (S.checkArgCount(Call, 6))
     return ExprError();
@@ -3498,6 +3533,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
     return PointerAuthSignGenericData(*this, TheCall);
   case Builtin::BI__builtin_ptrauth_auth_and_resign:
     return PointerAuthAuthAndResign(*this, TheCall);
+  case Builtin::BI__builtin_ptrauth_auth_with_pc_and_resign:
+    return PointerAuthAuthWithPCAndResign(*this, TheCall);
   case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
     return PointerAuthAuthLoadRelativeAndSign(*this, TheCall);
   case Builtin::BI__builtin_ptrauth_string_discriminator:
diff --git a/clang/test/CodeGen/ptrauth-intrinsics.c 
b/clang/test/CodeGen/ptrauth-intrinsics.c
index bd348f9b3551a..c98ce60dc9fc9 100644
--- a/clang/test/CodeGen/ptrauth-intrinsics.c
+++ b/clang/test/CodeGen/ptrauth-intrinsics.c
@@ -67,6 +67,20 @@ void test_auth_load_relative_and_sign() {
   fnptr = __builtin_ptrauth_auth_load_relative_and_sign(fnptr, 0, 
ptr_discriminator, 3, 15, 16L);
 }
 
+// CHECK-LABEL: define {{.*}}void @test_auth_with_pc_and_resign()
+void test_auth_with_pc_and_resign() {
+  // CHECK:      [[PTR:%.*]] = load ptr, ptr @fnptr,
+  // CHECK-NEXT: [[DISC0:%.*]] = load ptr, ptr @ptr_discriminator,
+  // CHECK-NEXT: [[PC:%.*]] = load ptr, ptr @ptr_discriminator,
+  // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
+  // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[DISC0]] to i64
+  // CHECK-NEXT: [[PCVAL:%.*]] = ptrtoint ptr [[PC]] to i64
+  // CHECK-NEXT: [[T1:%.*]] = call i64 
@llvm.ptrauth.auth.with.pc.and.resign(i64 [[T0]], i32 0, i64 [[DISC]], i64 
[[PCVAL]], i32 3, i64 15)
+  // CHECK-NEXT: [[RESULT:%.*]] = inttoptr  i64 [[T1]] to ptr
+  // CHECK-NEXT: store ptr [[RESULT]], ptr @fnptr,
+  fnptr = __builtin_ptrauth_auth_with_pc_and_resign(fnptr, 0, 
ptr_discriminator, ptr_discriminator, 3, 15);
+}
+
 // CHECK-LABEL: define {{.*}}void @test_blend_discriminator()
 void test_blend_discriminator() {
   // CHECK:      [[PTR:%.*]] = load ptr, ptr @fnptr,
diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c 
b/clang/test/Sema/ptrauth-intrinsics-macro.c
index adbb71a9d6e50..fbe1149eec786 100644
--- a/clang/test/Sema/ptrauth-intrinsics-macro.c
+++ b/clang/test/Sema/ptrauth-intrinsics-macro.c
@@ -16,12 +16,17 @@ void test(int *dp, int value) {
   (void)t0;
   dp = ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0);
   dp = ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp);
+  dp = ptrauth_auth_with_pc_and_resign(dp, VALID_CODE_KEY, dp, dp, 
VALID_DATA_KEY, dp);
   dp = ptrauth_auth_data(dp, VALID_DATA_KEY, 0);
   int pu0 = 0, pu1 = 0, pu2 = 0, pu3 = 0, pu4 = 0, pu5 = 0, pu6 = 0, pu7 = 0;
   ptrauth_blend_discriminator(&pu0, value);
   ptrauth_auth_and_resign(&pu1, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp);
   ptrauth_auth_and_resign(dp, VALID_DATA_KEY, &pu2, VALID_DATA_KEY, dp);
   ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, &pu3);
+  ptrauth_auth_with_pc_and_resign(&pu1, VALID_CODE_KEY, dp, dp, 
VALID_DATA_KEY, dp);
+  ptrauth_auth_with_pc_and_resign(dp, VALID_CODE_KEY, &pu2, dp, 
VALID_DATA_KEY, dp);
+  ptrauth_auth_with_pc_and_resign(dp, VALID_CODE_KEY, dp, &pu3, 
VALID_DATA_KEY, dp);
+  ptrauth_auth_with_pc_and_resign(dp, VALID_CODE_KEY, dp, dp, VALID_DATA_KEY, 
&pu4);
   ptrauth_sign_generic_data(pu4, dp);
   ptrauth_sign_generic_data(dp, pu5);
   ptrauth_auth_data(&pu6, VALID_DATA_KEY, value);
diff --git a/clang/test/Sema/ptrauth.c b/clang/test/Sema/ptrauth.c
index 59c18a3ef5e40..5aae042f50c96 100644
--- a/clang/test/Sema/ptrauth.c
+++ b/clang/test/Sema/ptrauth.c
@@ -145,6 +145,33 @@ void test_auth_load_relative_and_sign(int *dp, int 
(*fp)(int)) {
   float *mismatch = __builtin_ptrauth_auth_load_relative_and_sign(dp, 
VALID_DATA_KEY, 0, VALID_DATA_KEY, dp,0); // expected-error {{incompatible 
pointer types initializing 'float *' with an expression of type 'int *'}}
 }
 
+void test_auth_with_pc_and_resign(int *dp, int (*fp)(int), void *pc) {
+  __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_DATA_KEY, 0, pc, 
VALID_DATA_KEY); // expected-error {{too few arguments}}
+  __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_DATA_KEY, dp, pc, 
VALID_DATA_KEY, dp, 0); // expected-error {{too many arguments}}
+
+  __builtin_ptrauth_auth_with_pc_and_resign(mismatched_type, VALID_DATA_KEY, 
0, pc, VALID_DATA_KEY, dp); // expected-error {{signed value must have pointer 
type; type here is 'struct A'}}
+  __builtin_ptrauth_auth_with_pc_and_resign(dp, mismatched_type, 0, pc, 
VALID_DATA_KEY, dp); // expected-error {{passing 'struct A' to parameter of 
incompatible type 'int'}}
+  __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_DATA_KEY, 
mismatched_type, pc, VALID_DATA_KEY, dp); // expected-error {{extra 
discriminator must have pointer or integer type; type here is 'struct A'}}
+  __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_DATA_KEY, 0, 
mismatched_type, VALID_DATA_KEY, dp); // expected-error {{blended pc must have 
pointer or integer type; type here is 'struct A'}}
+  __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_DATA_KEY, 0, pc, 
mismatched_type, dp); // expected-error {{passing 'struct A' to parameter of 
incompatible type 'int'}}
+  __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_DATA_KEY, 0, pc, 
VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must 
have pointer or integer type; type here is 'struct A'}}
+
+  (void) __builtin_ptrauth_auth_with_pc_and_resign(NULL, VALID_DATA_KEY, 0, 
pc, VALID_DATA_KEY, dp); // expected-error {{ptrauth_auth_with_pc_and_resign 
only supports auth with IA and IB keys, not 2}} expected-warning 
{{authenticating a null pointer will almost certainly trap}}
+
+  // Test that data keys (DA/DB) are rejected for oldKey
+  int *dr = __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_DATA_KEY, 0, 
pc, VALID_DATA_KEY, dp); // expected-error {{ptrauth_auth_with_pc_and_resign 
only supports auth with IA and IB keys, not 2}}
+  dr = __builtin_ptrauth_auth_with_pc_and_resign(dp, 3, 0, pc, VALID_DATA_KEY, 
dp); // expected-error {{ptrauth_auth_with_pc_and_resign only supports auth 
with IA and IB keys, not 3}}
+  dr = __builtin_ptrauth_auth_with_pc_and_resign(dp, INVALID_KEY, 0, pc, 
VALID_DATA_KEY, dp); // expected-error {{does not identify a valid pointer 
authentication key for the current target}}
+  dr = __builtin_ptrauth_auth_with_pc_and_resign(dp, VALID_CODE_KEY, 0, pc, 
INVALID_KEY, dp); // expected-error {{does not identify a valid pointer 
authentication key for the current target}}
+
+  // Test with valid IA/IB keys
+  int (*fr)(int) = __builtin_ptrauth_auth_with_pc_and_resign(fp, 
VALID_CODE_KEY, 0, pc, VALID_CODE_KEY, dp);
+  fr = __builtin_ptrauth_auth_with_pc_and_resign(fp, INVALID_KEY, 0, pc, 
VALID_CODE_KEY, dp); // expected-error {{does not identify a valid pointer 
authentication key for the current target}}
+  fr = __builtin_ptrauth_auth_with_pc_and_resign(fp, VALID_CODE_KEY, 0, pc, 
INVALID_KEY, dp); // expected-error {{does not identify a valid pointer 
authentication key for the current target}}
+
+  float *mismatch = __builtin_ptrauth_auth_with_pc_and_resign(dp, 
VALID_CODE_KEY, 0, pc, VALID_DATA_KEY, dp); // expected-error {{incompatible 
pointer types initializing 'float *' with an expression of type 'int *'}}
+}
+
 void test_sign_generic_data(int *dp) {
   __builtin_ptrauth_sign_generic_data(dp); // expected-error {{too few 
arguments}}
   __builtin_ptrauth_sign_generic_data(dp, 0, 0); // expected-error {{too many 
arguments}}
diff --git a/llvm/include/llvm/IR/Intrinsics.td 
b/llvm/include/llvm/IR/Intrinsics.td
index b1b2bb2a72c65..b6d41fb543af9 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1839,6 +1839,19 @@ def int_experimental_patchpoint : 
Intrinsic<[llvm_any_ty],
                                              ImmArg<ArgIndex<1>>,
                                              ImmArg<ArgIndex<3>>]>;
 
+// Authenticate a signed pointer using a PC-based signature and resign it.
+// The second (key) and third (discriminator) arguments specify the signing
+// schema used for authenticating.
+// The fourth argument specifies the signing PC value.
+// The fifth and sixth arguments specify the schema used for resigning.
+// The signature must be valid.
+// This uses autia171615/autib171615 for authentication with PC, then signs 
normally.
+def int_ptrauth_auth_with_pc_and_resign : Intrinsic<[llvm_i64_ty],
+                                                     [llvm_i64_ty, 
llvm_i32_ty, llvm_i64_ty,
+                                                      llvm_i64_ty, 
llvm_i32_ty, llvm_i64_ty],
+                                                     [IntrNoMem, 
ImmArg<ArgIndex<1>>,
+                                                      ImmArg<ArgIndex<4>>]>;
+
 
 //===------------------------ Garbage Collection Intrinsics 
---------------===//
 // These are documented in docs/Statepoint.rst
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index f0363b6553440..c30398e905c39 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -7458,6 +7458,14 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, 
CallBase &Call) {
           &Call);
     break;
   }
+  case Intrinsic::ptrauth_auth_with_pc_and_resign: {
+    // Verify that the auth key is IA (0) or IB (1), not DA (2) or DB (3)
+    auto *AuthKey = cast<ConstantInt>(Call.getArgOperand(1));
+    uint64_t Key = AuthKey->getZExtValue();
+    Check(Key == 0 || Key == 1,
+          "ptrauth.auth.with.pc.and.resign key must be IA (0) or IB (1)", 
&Call);
+    break;
+  }
   };
 
   // Verify that there aren't any unmediated control transfers between 
funclets.
diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp 
b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 43f97dd3b60e3..524e9a8e34879 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -367,6 +367,7 @@ class AArch64DAGToDAGISel : public SelectionDAGISel {
 
   void SelectPtrauthAuth(SDNode *N);
   void SelectPtrauthResign(SDNode *N);
+  void SelectPtrauthResignWithPC(SDNode *N);
 
   bool trySelectStackSlotTagP(SDNode *N);
   void SelectTagP(SDNode *N);
@@ -1761,6 +1762,45 @@ void AArch64DAGToDAGISel::SelectPtrauthResign(SDNode *N) 
{
   }
 }
 
+void AArch64DAGToDAGISel::SelectPtrauthResignWithPC(SDNode *N) {
+  SDLoc DL(N);
+  SDValue Val = N->getOperand(1);
+  SDValue AUTKey = N->getOperand(2);
+  SDValue AUTDisc = N->getOperand(3);
+  SDValue AUTPC = N->getOperand(4);
+  SDValue PACKey = N->getOperand(5);
+  SDValue PACDisc = N->getOperand(6);
+
+  unsigned AUTKeyC = cast<ConstantSDNode>(AUTKey)->getZExtValue();
+  unsigned PACKeyC = cast<ConstantSDNode>(PACKey)->getZExtValue();
+
+  AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64);
+  PACKey = CurDAG->getTargetConstant(PACKeyC, DL, MVT::i64);
+
+  SDValue AUTAddrDisc, AUTConstDisc;
+  std::tie(AUTConstDisc, AUTAddrDisc) =
+      extractPtrauthBlendDiscriminators(AUTDisc, CurDAG);
+
+  SDValue PACAddrDisc, PACConstDisc;
+  std::tie(PACConstDisc, PACAddrDisc) =
+      extractPtrauthBlendDiscriminators(PACDisc, CurDAG);
+
+  SDValue X17Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
+                                         AArch64::X17, Val, SDValue());
+  SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
+                                         AArch64::X16, AUTAddrDisc, 
X17Copy.getValue(1));
+  SDValue X15Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
+                                         AArch64::X15, AUTPC, 
X16Copy.getValue(1));
+
+  unsigned AuthOpc = (AUTKeyC == 0) ? AArch64::AUTIA171615 : 
AArch64::AUTIB171615;
+  SDNode *AUTH = CurDAG->getMachineNode(AuthOpc, DL, MVT::i64, 
X15Copy.getValue(1));
+
+  SDValue AuthedVal = SDValue(AUTH, 0);
+  SDValue Ops[] = {AuthedVal, PACKey, PACConstDisc, PACAddrDisc};
+  SDNode *PAC = CurDAG->getMachineNode(AArch64::PAC, DL, MVT::i64, Ops);
+  ReplaceNode(N, PAC);
+}
+
 bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) {
   LoadSDNode *LD = cast<LoadSDNode>(N);
   if (LD->isUnindexed())
@@ -6034,6 +6074,10 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) {
       SelectPtrauthResign(Node);
       return;
 
+    case Intrinsic::ptrauth_auth_with_pc_and_resign:
+      SelectPtrauthResignWithPC(Node);
+      return;
+
     case Intrinsic::aarch64_neon_tbl2:
       SelectTable(Node, 2,
                   VT == MVT::v8i8 ? AArch64::TBLv8i8Two : AArch64::TBLv16i8Two,
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp 
b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index cf650fd5c4e72..20f066ba71fc2 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -6694,6 +6694,67 @@ bool 
AArch64InstructionSelector::selectIntrinsic(MachineInstr &I,
     I.eraseFromParent();
     return true;
   }
+  case Intrinsic::ptrauth_auth_with_pc_and_resign: {
+    // ptrauth.auth.with.pc.and.resign: authenticate with PC, then sign
+    // Operands: value, authKey, authDisc, authPC, signKey, signDisc
+    Register DstReg = I.getOperand(0).getReg();
+    Register ValReg = I.getOperand(2).getReg();
+    uint64_t AUTKey = I.getOperand(3).getImm();
+    Register AUTDisc = I.getOperand(4).getReg();
+    Register AUTPC = I.getOperand(5).getReg();
+    uint64_t PACKey = I.getOperand(6).getImm();
+    Register PACDisc = I.getOperand(7).getReg();
+
+    // Extract discriminators
+    Register AUTAddrDisc = AUTDisc;
+    uint16_t AUTConstDiscC = 0;
+    std::tie(AUTConstDiscC, AUTAddrDisc) =
+        extractPtrauthBlendDiscriminators(AUTDisc, MRI);
+
+    Register PACAddrDisc = PACDisc;
+    uint16_t PACConstDiscC = 0;
+    std::tie(PACConstDiscC, PACAddrDisc) =
+        extractPtrauthBlendDiscriminators(PACDisc, MRI);
+
+    // Set up autia171615/autib171615: x17=value, x16=disc, x15=pc
+    MIB.buildCopy({AArch64::X17}, {ValReg});
+
+    // Handle discriminator - if NoRegister, use XZR
+    if (AUTAddrDisc == AArch64::NoRegister)
+      AUTAddrDisc = AArch64::XZR;
+    MIB.buildCopy({AArch64::X16}, {AUTAddrDisc});
+
+    MIB.buildCopy({AArch64::X15}, {AUTPC});
+
+    // Use the appropriate autia/autib instruction based on the key
+    // Note: Only IA/IB keys supported for 171615 instructions, not DA/DB
+    assert((AUTKey == 0 || AUTKey == 1) &&
+           "auth_with_pc_and_resign only supports IA and IB keys");
+    unsigned AuthOpc = (AUTKey == 0) ? AArch64::AUTIA171615
+                                      : AArch64::AUTIB171615;
+    MIB.buildInstr(AuthOpc).constrainAllUses(TII, TRI, RBI);
+
+    // Now sign the authenticated value (result is in X17)
+    // Use the PAC pseudo which handles the signing
+    Register AuthedReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
+    MIB.buildCopy({AuthedReg}, Register(AArch64::X17));
+
+    // Handle sign discriminator - if NoRegister, use XZR
+    if (PACAddrDisc == AArch64::NoRegister)
+      PACAddrDisc = AArch64::XZR;
+
+    MIB.buildInstr(AArch64::PAC)
+        .addDef(DstReg)
+        .addUse(AuthedReg)
+        .addImm(PACKey)
+        .addImm(PACConstDiscC)
+        .addUse(PACAddrDisc)
+        .constrainAllUses(TII, TRI, RBI);
+
+    RBI.constrainGenericRegister(DstReg, AArch64::GPR64RegClass, MRI);
+    I.eraseFromParent();
+    return true;
+  }
   case Intrinsic::ptrauth_auth: {
     Register DstReg = I.getOperand(0).getReg();
     Register ValReg = I.getOperand(2).getReg();
diff --git 
a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll 
b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll
new file mode 100644
index 0000000000000..fed448465a151
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll
@@ -0,0 +1,93 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr 
-verify-machineinstrs | FileCheck %s
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel 
-global-isel-abort=1 -verify-machineinstrs | FileCheck %s
+
+; TODO: The 171615 instructions are always unchecked, so auth-check modes 
don't change output
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+
+define i64 @test_resign_with_pc_ia_ia(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
+; CHECK-LABEL: test_resign_with_pc_ia_ia:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    mov x17, x0
+; CHECK-NEXT:    mov x16, x1
+; CHECK-NEXT:    mov x15, x2
+; CHECK-NEXT:    autia171615
+; CHECK-NEXT:    mov x0, x17
+; CHECK-NEXT:    pacia x0, x3
+; CHECK-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
%arg1, i64 %arg2, i32 0, i64 %arg3)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_with_pc_ib_ia(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
+; CHECK-LABEL: test_resign_with_pc_ib_ia:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    mov x17, x0
+; CHECK-NEXT:    mov x16, x1
+; CHECK-NEXT:    mov x15, x2
+; CHECK-NEXT:    autib171615
+; CHECK-NEXT:    mov x0, x17
+; CHECK-NEXT:    pacia x0, x3
+; CHECK-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 1, i64 
%arg1, i64 %arg2, i32 0, i64 %arg3)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_with_pc_ia_ib(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
+; CHECK-LABEL: test_resign_with_pc_ia_ib:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    mov x17, x0
+; CHECK-NEXT:    mov x16, x1
+; CHECK-NEXT:    mov x15, x2
+; CHECK-NEXT:    autia171615
+; CHECK-NEXT:    mov x0, x17
+; CHECK-NEXT:    pacib x0, x3
+; CHECK-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
%arg1, i64 %arg2, i32 1, i64 %arg3)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_with_pc_ib_ib(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
+; CHECK-LABEL: test_resign_with_pc_ib_ib:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    mov x17, x0
+; CHECK-NEXT:    mov x16, x1
+; CHECK-NEXT:    mov x15, x2
+; CHECK-NEXT:    autib171615
+; CHECK-NEXT:    mov x0, x17
+; CHECK-NEXT:    pacib x0, x3
+; CHECK-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 1, i64 
%arg1, i64 %arg2, i32 1, i64 %arg3)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_with_pc_iza_ib(i64 %arg, i64 %arg1, i64 %arg2) {
+; CHECK-LABEL: test_resign_with_pc_iza_ib:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    mov x17, x0
+; CHECK-NEXT:    mov x16, #0 ; =0x0
+; CHECK-NEXT:    mov x15, x1
+; CHECK-NEXT:    autia171615
+; CHECK-NEXT:    mov x0, x17
+; CHECK-NEXT:    pacib x0, x2
+; CHECK-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
0, i64 %arg1, i32 1, i64 %arg2)
+  ret i64 %tmp
+}
+
+define i64 @test_resign_with_pc_ia_izb(i64 %arg, i64 %arg1, i64 %arg2) {
+; CHECK-LABEL: test_resign_with_pc_ia_izb:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    mov x17, x0
+; CHECK-NEXT:    mov x16, x1
+; CHECK-NEXT:    mov x15, x2
+; CHECK-NEXT:    autia171615
+; CHECK-NEXT:    mov x0, x17
+; CHECK-NEXT:    pacizb x0
+; CHECK-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
%arg1, i64 %arg2, i32 1, i64 0)
+  ret i64 %tmp
+}
+
+declare i64 @llvm.ptrauth.auth.with.pc.and.resign(i64, i32, i64, i64, i32, i64)

>From e34e459a8b97ae5ad5042951f279e872c7f70ace Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Tue, 9 Jun 2026 12:04:54 -0700
Subject: [PATCH 2/9] clang-format

---
 clang/lib/CodeGen/CGBuiltin.cpp                 |  3 ++-
 clang/lib/Headers/ptrauth.h                     | 10 ++++++----
 llvm/lib/IR/Verifier.cpp                        |  3 ++-
 llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp | 17 ++++++++++-------
 .../GISel/AArch64InstructionSelector.cpp        |  4 ++--
 5 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 84103f3d4f6b3..5f2ba6412da78 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6267,7 +6267,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl 
GD, unsigned BuiltinID,
 
     switch (BuiltinID) {
     case Builtin::BI__builtin_ptrauth_auth_with_pc_and_resign:
-      // Convert oldDiscriminator (arg 2), oldPC (arg 3) and newDiscriminator 
(arg 5) to intptr_t
+      // Convert oldDiscriminator (arg 2), oldPC (arg 3) and newDiscriminator
+      // (arg 5) to intptr_t
       if (Args[2]->getType()->isPointerTy())
         Args[2] = Builder.CreatePtrToInt(Args[2], IntPtrTy);
       if (Args[3]->getType()->isPointerTy())
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index 2b9201d1c8a2b..3b34c42b22fb3 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -200,8 +200,10 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 
    Do not pass a null pointer to this function. A null pointer
    will not successfully authenticate. */
-#define ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data, 
__old_pc, __new_key, __new_data) \
-  __builtin_ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data, 
__old_pc, __new_key, __new_data)
+#define ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data,        
\
+                                        __old_pc, __new_key, __new_data)       
\
+  __builtin_ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data,    
\
+                                            __old_pc, __new_key, __new_data)
 
 /* Authenticate a pointer using one scheme, load 32bit value at offset addend
    from the pointer, and add this value to the pointer, sign using specified
@@ -421,8 +423,8 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
     __value;                                                                   
\
   })
 
-#define ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data, 
__old_pc, \
-                                        __new_key, __new_data)                 
\
+#define ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data,        
\
+                                        __old_pc, __new_key, __new_data)       
\
   ({                                                                           
\
     (void)__old_key;                                                           
\
     (void)__old_data;                                                          
\
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c30398e905c39..ca681cb4b572c 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -7463,7 +7463,8 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, 
CallBase &Call) {
     auto *AuthKey = cast<ConstantInt>(Call.getArgOperand(1));
     uint64_t Key = AuthKey->getZExtValue();
     Check(Key == 0 || Key == 1,
-          "ptrauth.auth.with.pc.and.resign key must be IA (0) or IB (1)", 
&Call);
+          "ptrauth.auth.with.pc.and.resign key must be IA (0) or IB (1)",
+          &Call);
     break;
   }
   };
diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp 
b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 524e9a8e34879..c70bd72617d4a 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -1787,13 +1787,16 @@ void 
AArch64DAGToDAGISel::SelectPtrauthResignWithPC(SDNode *N) {
 
   SDValue X17Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
                                          AArch64::X17, Val, SDValue());
-  SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
-                                         AArch64::X16, AUTAddrDisc, 
X17Copy.getValue(1));
-  SDValue X15Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
-                                         AArch64::X15, AUTPC, 
X16Copy.getValue(1));
-
-  unsigned AuthOpc = (AUTKeyC == 0) ? AArch64::AUTIA171615 : 
AArch64::AUTIB171615;
-  SDNode *AUTH = CurDAG->getMachineNode(AuthOpc, DL, MVT::i64, 
X15Copy.getValue(1));
+  SDValue X16Copy =
+      CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X16,
+                           AUTAddrDisc, X17Copy.getValue(1));
+  SDValue X15Copy = CurDAG->getCopyToReg(
+      CurDAG->getEntryNode(), DL, AArch64::X15, AUTPC, X16Copy.getValue(1));
+
+  unsigned AuthOpc =
+      (AUTKeyC == 0) ? AArch64::AUTIA171615 : AArch64::AUTIB171615;
+  SDNode *AUTH =
+      CurDAG->getMachineNode(AuthOpc, DL, MVT::i64, X15Copy.getValue(1));
 
   SDValue AuthedVal = SDValue(AUTH, 0);
   SDValue Ops[] = {AuthedVal, PACKey, PACConstDisc, PACAddrDisc};
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp 
b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index 20f066ba71fc2..0e7b070133105 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -6730,8 +6730,8 @@ bool 
AArch64InstructionSelector::selectIntrinsic(MachineInstr &I,
     // Note: Only IA/IB keys supported for 171615 instructions, not DA/DB
     assert((AUTKey == 0 || AUTKey == 1) &&
            "auth_with_pc_and_resign only supports IA and IB keys");
-    unsigned AuthOpc = (AUTKey == 0) ? AArch64::AUTIA171615
-                                      : AArch64::AUTIB171615;
+    unsigned AuthOpc =
+        (AUTKey == 0) ? AArch64::AUTIA171615 : AArch64::AUTIB171615;
     MIB.buildInstr(AuthOpc).constrainAllUses(TII, TRI, RBI);
 
     // Now sign the authenticated value (result is in X17)

>From 35cd4ba0ea5b919c72f67e6a8b259af266c65879 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Thu, 11 Jun 2026 12:09:16 -0700
Subject: [PATCH 3/9] __extension__

---
 clang/lib/Headers/ptrauth.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index 3b34c42b22fb3..c521c3efcf0f9 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -425,7 +425,7 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
 
 #define ptrauth_auth_with_pc_and_resign(__value, __old_key, __old_data,        
\
                                         __old_pc, __new_key, __new_data)       
\
-  ({                                                                           
\
+  __extension__({                                                              
\
     (void)__old_key;                                                           
\
     (void)__old_data;                                                          
\
     (void)__old_pc;                                                            
\

>From 67af0015381fe85ac4e1f0428c3aea59b7899a30 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Thu, 11 Jun 2026 12:34:39 -0700
Subject: [PATCH 4/9] separate ptr_discriminator/pc_discriminator

---
 clang/test/CodeGen/ptrauth-intrinsics.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/test/CodeGen/ptrauth-intrinsics.c 
b/clang/test/CodeGen/ptrauth-intrinsics.c
index c98ce60dc9fc9..0ecf33c210586 100644
--- a/clang/test/CodeGen/ptrauth-intrinsics.c
+++ b/clang/test/CodeGen/ptrauth-intrinsics.c
@@ -7,6 +7,7 @@
 void (*fnptr)(void);
 long int_discriminator;
 void *ptr_discriminator;
+void *pc_discriminator;
 long signature;
 
 // CHECK-LABEL: define {{.*}}void @test_auth()
@@ -71,14 +72,14 @@ void test_auth_load_relative_and_sign() {
 void test_auth_with_pc_and_resign() {
   // CHECK:      [[PTR:%.*]] = load ptr, ptr @fnptr,
   // CHECK-NEXT: [[DISC0:%.*]] = load ptr, ptr @ptr_discriminator,
-  // CHECK-NEXT: [[PC:%.*]] = load ptr, ptr @ptr_discriminator,
+  // CHECK-NEXT: [[PC:%.*]] = load ptr, ptr @pc_discriminator,
   // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[PTR]] to i64
   // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[DISC0]] to i64
   // CHECK-NEXT: [[PCVAL:%.*]] = ptrtoint ptr [[PC]] to i64
   // CHECK-NEXT: [[T1:%.*]] = call i64 
@llvm.ptrauth.auth.with.pc.and.resign(i64 [[T0]], i32 0, i64 [[DISC]], i64 
[[PCVAL]], i32 3, i64 15)
   // CHECK-NEXT: [[RESULT:%.*]] = inttoptr  i64 [[T1]] to ptr
   // CHECK-NEXT: store ptr [[RESULT]], ptr @fnptr,
-  fnptr = __builtin_ptrauth_auth_with_pc_and_resign(fnptr, 0, 
ptr_discriminator, ptr_discriminator, 3, 15);
+  fnptr = __builtin_ptrauth_auth_with_pc_and_resign(fnptr, 0, 
ptr_discriminator, pc_discriminator, 3, 15);
 }
 
 // CHECK-LABEL: define {{.*}}void @test_blend_discriminator()

>From 66f8132989c6c90017648e578298b19382cfc190 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Thu, 11 Jun 2026 15:11:03 -0700
Subject: [PATCH 5/9] pseudo

---
 llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp |  81 +++-
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    |  21 +-
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  18 +
 .../GISel/AArch64InstructionSelector.cpp      |  43 +-
 ...rauth-intrinsic-auth-with-pc-and-resign.ll | 381 +++++++++++++++---
 5 files changed, 430 insertions(+), 114 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp 
b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index b16c0460adf38..47b57721bb635 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -190,13 +190,19 @@ class AArch64AsmPrinter : public AsmPrinter {
   void emitPtrauthTailCallHardening(const MachineInstr *TC);
 
   struct PtrAuthSchema {
+    // Standard schema: explicit address-discriminator operand, no PC blending.
     PtrAuthSchema(AArch64PACKey::ID Key, uint64_t IntDisc,
                   const MachineOperand &AddrDiscOp);
 
+    // PC-blending schema (for auti[ab]171615): x16 is the implicit address
+    // discriminator; PCDisc (typically x15) carries the PC half.
+    PtrAuthSchema(AArch64PACKey::ID Key, uint64_t IntDisc, Register PCDisc);
+
     AArch64PACKey::ID Key;
     uint64_t IntDisc;
     Register AddrDisc;
     bool AddrDiscIsKilled;
+    Register PCDisc;
   };
 
   // Helper for emitting AUTRELLOADPAC: increment Pointer by Addend and then by
@@ -209,7 +215,8 @@ class AArch64AsmPrinter : public AsmPrinter {
   void emitPtrauthApplyIndirectAddend(Register Pointer, Register Scratch,
                                       int64_t Addend);
 
-  // Emit the sequence for AUT or AUTPAC. Addend if AUTRELLOADPAC
+  // Emit the sequence for AUT or AUTPAC (or their PC-blending variants).
+  // Addend is only used for AUTRELLOADPAC.
   void emitPtrauthAuthResign(Register Pointer, Register Scratch,
                              PtrAuthSchema AuthSchema,
                              std::optional<PtrAuthSchema> SignSchema,
@@ -2240,7 +2247,13 @@ bool 
AArch64AsmPrinter::emitDeactivationSymbolRelocation(Value *DS) {
 AArch64AsmPrinter::PtrAuthSchema::PtrAuthSchema(
     AArch64PACKey::ID Key, uint64_t IntDisc, const MachineOperand &AddrDiscOp)
     : Key(Key), IntDisc(IntDisc), AddrDisc(AddrDiscOp.getReg()),
-      AddrDiscIsKilled(AddrDiscOp.isKill()) {}
+      AddrDiscIsKilled(AddrDiscOp.isKill()), PCDisc(AArch64::NoRegister) {}
+
+AArch64AsmPrinter::PtrAuthSchema::PtrAuthSchema(AArch64PACKey::ID Key,
+                                                uint64_t IntDisc,
+                                                Register PCDisc)
+    : Key(Key), IntDisc(IntDisc), AddrDisc(AArch64::X16),
+      AddrDiscIsKilled(false), PCDisc(PCDisc) {}
 
 void AArch64AsmPrinter::emitPtrauthApplyIndirectAddend(Register Pointer,
                                                        Register Scratch,
@@ -2300,12 +2313,14 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(
     std::optional<PtrAuthSchema> SignSchema, std::optional<int64_t> Addend,
     Value *DS) {
   const bool IsResign = SignSchema.has_value();
-  // We expand AUT/AUTPAC into a sequence of the form
+  const bool WithPC = AuthSchema.PCDisc != AArch64::NoRegister;
+
+  // We expand AUT/AUTPAC (and their PC-blending variants) into:
   //
-  //      ; authenticate x16
-  //      ; check pointer in x16
+  //      ; authenticate Pointer
+  //      ; check Pointer
   //    Lsuccess:
-  //      ; sign x16 (if AUTPAC)
+  //      ; sign Pointer (if resign)
   //    Lend:   ; if not trapping on failure
   //
   // with the checking sequence chosen depending on whether/how we should check
@@ -2337,13 +2352,34 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(
     break;
   }
 
-  // Compute aut discriminator
-  Register AUTDiscReg =
-      emitPtrauthDiscriminator(AuthSchema.IntDisc, AuthSchema.AddrDisc, 
Scratch,
-                               AuthSchema.AddrDiscIsKilled);
-
-  if (!emitDeactivationSymbolRelocation(DS))
-    emitAUT(AuthSchema.Key, Pointer, AUTDiscReg);
+  if (WithPC) {
+    // auti[ab]171615: pointer is in x17, address discriminator in x16,
+    // PC discriminator in x15.
+    assert(Pointer == AArch64::X17 && Scratch == AArch64::X16 &&
+           "AUTPCPAC must use x17/x16 as Pointer/Scratch");
+
+    // Blend the constant discriminator into x16 in-place.
+    Register AUTDiscReg =
+        emitPtrauthDiscriminator(AuthSchema.IntDisc, Scratch, Scratch,
+                                 /*MayClobberAddrDisc=*/true);
+    assert((AUTDiscReg == Scratch || AUTDiscReg == AArch64::XZR) &&
+           "AUT discriminator must be in x16 for auti[ab]171615");
+    (void)AUTDiscReg;
+
+    if (!emitDeactivationSymbolRelocation(DS)) {
+      unsigned AutOpc = (AuthSchema.Key == AArch64PACKey::IB)
+                            ? AArch64::AUTIB171615
+                            : AArch64::AUTIA171615;
+      EmitToStreamer(MCInstBuilder(AutOpc));
+    }
+  } else {
+    // Standard AUT: discriminator computed into Scratch, then auti[ab].
+    Register AUTDiscReg =
+        emitPtrauthDiscriminator(AuthSchema.IntDisc, AuthSchema.AddrDisc,
+                                 Scratch, AuthSchema.AddrDiscIsKilled);
+    if (!emitDeactivationSymbolRelocation(DS))
+      emitAUT(AuthSchema.Key, Pointer, AUTDiscReg);
+  }
 
   // Unchecked or checked-but-non-trapping AUT is just an "AUT": we're done.
   if (!IsResign && (!ShouldCheck || !ShouldTrap))
@@ -2361,7 +2397,7 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(
   }
 
   // We already emitted unchecked and checked-but-non-trapping AUTs.
-  // That left us with trapping AUTs, and AUTPA/AUTRELLOADPACs.
+  // That left us with trapping AUTs, and AUTPAC/AUTRELLOADPACs.
   // Trapping AUTs don't need PAC: we're done.
   if (!IsResign)
     return;
@@ -2369,7 +2405,7 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(
   if (Addend.has_value())
     emitPtrauthApplyIndirectAddend(Pointer, Scratch, *Addend);
 
-  // Compute pac discriminator into x17
+  // Compute PAC discriminator into Scratch, then re-sign Pointer.
   Register PACDiscReg = emitPtrauthDiscriminator(SignSchema->IntDisc,
                                                  SignSchema->AddrDisc, 
Scratch);
   emitPAC(SignSchema->Key, Pointer, PACDiscReg);
@@ -3334,6 +3370,21 @@ void AArch64AsmPrinter::emitInstruction(const 
MachineInstr *MI) {
     return;
   }
 
+  case AArch64::AUTPCPAC: {
+    // x17 = pointer, x16 = AUTAddrDisc, x15 = PC discriminator (all implicit).
+    PtrAuthSchema AuthSchema((AArch64PACKey::ID)MI->getOperand(0).getImm(),
+                             MI->getOperand(1).getImm(),
+                             /*PCDisc=*/AArch64::X15);
+
+    PtrAuthSchema SignSchema((AArch64PACKey::ID)MI->getOperand(2).getImm(),
+                             MI->getOperand(3).getImm(), MI->getOperand(4));
+
+    emitPtrauthAuthResign(/*Pointer=*/AArch64::X17, /*Scratch=*/AArch64::X16,
+                          AuthSchema, SignSchema, std::nullopt,
+                          MI->getDeactivationSymbol());
+    return;
+  }
+
   case AArch64::AUTRELLOADPAC: {
     const Register Pointer = AArch64::X16;
     const Register Scratch = AArch64::X17;
diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp 
b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index c70bd72617d4a..b2deefd50e9e4 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -1790,18 +1790,15 @@ void 
AArch64DAGToDAGISel::SelectPtrauthResignWithPC(SDNode *N) {
   SDValue X16Copy =
       CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X16,
                            AUTAddrDisc, X17Copy.getValue(1));
-  SDValue X15Copy = CurDAG->getCopyToReg(
-      CurDAG->getEntryNode(), DL, AArch64::X15, AUTPC, X16Copy.getValue(1));
-
-  unsigned AuthOpc =
-      (AUTKeyC == 0) ? AArch64::AUTIA171615 : AArch64::AUTIB171615;
-  SDNode *AUTH =
-      CurDAG->getMachineNode(AuthOpc, DL, MVT::i64, X15Copy.getValue(1));
-
-  SDValue AuthedVal = SDValue(AUTH, 0);
-  SDValue Ops[] = {AuthedVal, PACKey, PACConstDisc, PACAddrDisc};
-  SDNode *PAC = CurDAG->getMachineNode(AArch64::PAC, DL, MVT::i64, Ops);
-  ReplaceNode(N, PAC);
+  SDValue X15Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
+                                         AArch64::X15, AUTPC,
+                                         X16Copy.getValue(1));
+
+  SDValue Ops[] = {AUTKey, AUTConstDisc, PACKey, PACConstDisc, PACAddrDisc,
+                   X15Copy.getValue(1)};
+  SDNode *AUTPCPAC =
+      CurDAG->getMachineNode(AArch64::AUTPCPAC, DL, MVT::i64, Ops);
+  ReplaceNode(N, AUTPCPAC);
 }
 
 bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) {
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td 
b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 200808665c93e..ceb288e1e0e49 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -2277,6 +2277,24 @@ let Predicates = [HasPAuth] in {
     let Uses = [X16];
   }
 
+  // AUT and re-PAC a value using different keys/data, with a PC value blended
+  // into the AUT'd discriminator.
+  // This directly manupulates x15,x16,x17, which are the only registers that
+  // certain OSs guarantee are safe to use for sensitive operations.
+  def AUTPCPAC
+      : Pseudo<(outs),
+               (ins i32imm:$AUTKey, i64imm:$AUTDisc,
+                    i32imm:$PACKey, i64imm:$PACDisc, GPR64noip:$PACAddrDisc),
+               []>, Sched<[WriteI, ReadI]> {
+    let isCodeGenOnly = 1;
+    let hasSideEffects = 1;
+    let mayStore = 0;
+    let mayLoad = 0;
+    let Size = 48;
+    let Defs = [X17,X15,X16,NZCV];
+    let Uses = [X15,X16,X17];
+  }
+
   // Similiar to AUTPAC, except a 32bit value is loaded at Addend offset from
   // pointer and this value is added to the pointer before signing. This
   // directly manipulates x16/x17, which are the only registers the OS
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp 
b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index 0e7b070133105..eea2e560aa68e 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -6695,8 +6695,6 @@ bool 
AArch64InstructionSelector::selectIntrinsic(MachineInstr &I,
     return true;
   }
   case Intrinsic::ptrauth_auth_with_pc_and_resign: {
-    // ptrauth.auth.with.pc.and.resign: authenticate with PC, then sign
-    // Operands: value, authKey, authDisc, authPC, signKey, signDisc
     Register DstReg = I.getOperand(0).getReg();
     Register ValReg = I.getOperand(2).getReg();
     uint64_t AUTKey = I.getOperand(3).getImm();
@@ -6705,52 +6703,37 @@ bool 
AArch64InstructionSelector::selectIntrinsic(MachineInstr &I,
     uint64_t PACKey = I.getOperand(6).getImm();
     Register PACDisc = I.getOperand(7).getReg();
 
-    // Extract discriminators
-    Register AUTAddrDisc = AUTDisc;
+    assert((AUTKey == AArch64PACKey::IA || AUTKey == AArch64PACKey::IB) &&
+           "auth_with_pc_and_resign only supports IA and IB keys");
+
     uint16_t AUTConstDiscC = 0;
+    Register AUTAddrDisc;
     std::tie(AUTConstDiscC, AUTAddrDisc) =
         extractPtrauthBlendDiscriminators(AUTDisc, MRI);
 
-    Register PACAddrDisc = PACDisc;
     uint16_t PACConstDiscC = 0;
+    Register PACAddrDisc;
     std::tie(PACConstDiscC, PACAddrDisc) =
         extractPtrauthBlendDiscriminators(PACDisc, MRI);
 
-    // Set up autia171615/autib171615: x17=value, x16=disc, x15=pc
-    MIB.buildCopy({AArch64::X17}, {ValReg});
-
-    // Handle discriminator - if NoRegister, use XZR
     if (AUTAddrDisc == AArch64::NoRegister)
       AUTAddrDisc = AArch64::XZR;
-    MIB.buildCopy({AArch64::X16}, {AUTAddrDisc});
-
-    MIB.buildCopy({AArch64::X15}, {AUTPC});
-
-    // Use the appropriate autia/autib instruction based on the key
-    // Note: Only IA/IB keys supported for 171615 instructions, not DA/DB
-    assert((AUTKey == 0 || AUTKey == 1) &&
-           "auth_with_pc_and_resign only supports IA and IB keys");
-    unsigned AuthOpc =
-        (AUTKey == 0) ? AArch64::AUTIA171615 : AArch64::AUTIB171615;
-    MIB.buildInstr(AuthOpc).constrainAllUses(TII, TRI, RBI);
-
-    // Now sign the authenticated value (result is in X17)
-    // Use the PAC pseudo which handles the signing
-    Register AuthedReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass);
-    MIB.buildCopy({AuthedReg}, Register(AArch64::X17));
-
-    // Handle sign discriminator - if NoRegister, use XZR
     if (PACAddrDisc == AArch64::NoRegister)
       PACAddrDisc = AArch64::XZR;
 
-    MIB.buildInstr(AArch64::PAC)
-        .addDef(DstReg)
-        .addUse(AuthedReg)
+    MIB.buildCopy({AArch64::X17}, {ValReg});
+    MIB.buildCopy({AArch64::X16}, {AUTAddrDisc});
+    MIB.buildCopy({AArch64::X15}, {AUTPC});
+
+    MIB.buildInstr(AArch64::AUTPCPAC)
+        .addImm(AUTKey)
+        .addImm(AUTConstDiscC)
         .addImm(PACKey)
         .addImm(PACConstDiscC)
         .addUse(PACAddrDisc)
         .constrainAllUses(TII, TRI, RBI);
 
+    MIB.buildCopy({DstReg}, Register(AArch64::X17));
     RBI.constrainGenericRegister(DstReg, AArch64::GPR64RegClass, MRI);
     I.eraseFromParent();
     return true;
diff --git 
a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll 
b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll
index fed448465a151..8184bb9c03c49 100644
--- a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll
+++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-with-pc-and-resign.ll
@@ -1,93 +1,360 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr 
-verify-machineinstrs | FileCheck %s
-; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel 
-global-isel-abort=1 -verify-machineinstrs | FileCheck %s
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel=0   
                 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=none | FileCheck %s 
--check-prefixes=UNCHECKED
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel 
-global-isel-abort=1 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=none | FileCheck %s 
--check-prefixes=UNCHECKED
 
-; TODO: The 171615 instructions are always unchecked, so auth-check modes 
don't change output
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel=0   
                 -verify-machineinstrs \
+; RUN:                                       | FileCheck %s 
--check-prefixes=CHECKED
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel 
-global-isel-abort=1 -verify-machineinstrs \
+; RUN:                                       | FileCheck %s 
--check-prefixes=CHECKED
+
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel=0   
                 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=trap   | FileCheck %s 
--check-prefixes=TRAP
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr -global-isel 
-global-isel-abort=1 -verify-machineinstrs \
+; RUN:   -aarch64-ptrauth-auth-checks=trap   | FileCheck %s 
--check-prefixes=TRAP
+
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr,+fpac 
-global-isel=0                    -verify-machineinstrs \
+; RUN:                                       | FileCheck %s 
--check-prefixes=UNCHECKED
+; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+pauth-lr,+fpac 
-global-isel -global-isel-abort=1 -verify-machineinstrs \
+; RUN:                                       | FileCheck %s 
--check-prefixes=UNCHECKED
 
 target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
 
 define i64 @test_resign_with_pc_ia_ia(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
-; CHECK-LABEL: test_resign_with_pc_ia_ia:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    mov x17, x0
-; CHECK-NEXT:    mov x16, x1
-; CHECK-NEXT:    mov x15, x2
-; CHECK-NEXT:    autia171615
-; CHECK-NEXT:    mov x0, x17
-; CHECK-NEXT:    pacia x0, x3
-; CHECK-NEXT:    ret
+; UNCHECKED-LABEL: test_resign_with_pc_ia_ia:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x17, x0
+; UNCHECKED-NEXT:    mov x16, x1
+; UNCHECKED-NEXT:    mov x15, x2
+; UNCHECKED-NEXT:    autia171615
+; UNCHECKED-NEXT:    pacia x17, x3
+; UNCHECKED-NEXT:    mov x0, x17
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_with_pc_ia_ia:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x17, x0
+; CHECKED-NEXT:    mov x16, x1
+; CHECKED-NEXT:    mov x15, x2
+; CHECKED-NEXT:    autia171615
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    xpaci x16
+; CHECKED-NEXT:    cmp x17, x16
+; CHECKED-NEXT:    b.eq Lauth_success_0
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    b Lresign_end_0
+; CHECKED-NEXT:  Lauth_success_0:
+; CHECKED-NEXT:    pacia x17, x3
+; CHECKED-NEXT:  Lresign_end_0:
+; CHECKED-NEXT:    mov x0, x17
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_with_pc_ia_ia:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x17, x0
+; TRAP-NEXT:    mov x16, x1
+; TRAP-NEXT:    mov x15, x2
+; TRAP-NEXT:    autia171615
+; TRAP-NEXT:    mov x16, x17
+; TRAP-NEXT:    xpaci x16
+; TRAP-NEXT:    cmp x17, x16
+; TRAP-NEXT:    b.eq Lauth_success_0
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_0:
+; TRAP-NEXT:    pacia x17, x3
+; TRAP-NEXT:    mov x0, x17
+; TRAP-NEXT:    ret
   %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
%arg1, i64 %arg2, i32 0, i64 %arg3)
   ret i64 %tmp
 }
 
 define i64 @test_resign_with_pc_ib_ia(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
-; CHECK-LABEL: test_resign_with_pc_ib_ia:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    mov x17, x0
-; CHECK-NEXT:    mov x16, x1
-; CHECK-NEXT:    mov x15, x2
-; CHECK-NEXT:    autib171615
-; CHECK-NEXT:    mov x0, x17
-; CHECK-NEXT:    pacia x0, x3
-; CHECK-NEXT:    ret
+; UNCHECKED-LABEL: test_resign_with_pc_ib_ia:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x17, x0
+; UNCHECKED-NEXT:    mov x16, x1
+; UNCHECKED-NEXT:    mov x15, x2
+; UNCHECKED-NEXT:    autib171615
+; UNCHECKED-NEXT:    pacia x17, x3
+; UNCHECKED-NEXT:    mov x0, x17
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_with_pc_ib_ia:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x17, x0
+; CHECKED-NEXT:    mov x16, x1
+; CHECKED-NEXT:    mov x15, x2
+; CHECKED-NEXT:    autib171615
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    xpaci x16
+; CHECKED-NEXT:    cmp x17, x16
+; CHECKED-NEXT:    b.eq Lauth_success_1
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    b Lresign_end_1
+; CHECKED-NEXT:  Lauth_success_1:
+; CHECKED-NEXT:    pacia x17, x3
+; CHECKED-NEXT:  Lresign_end_1:
+; CHECKED-NEXT:    mov x0, x17
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_with_pc_ib_ia:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x17, x0
+; TRAP-NEXT:    mov x16, x1
+; TRAP-NEXT:    mov x15, x2
+; TRAP-NEXT:    autib171615
+; TRAP-NEXT:    mov x16, x17
+; TRAP-NEXT:    xpaci x16
+; TRAP-NEXT:    cmp x17, x16
+; TRAP-NEXT:    b.eq Lauth_success_1
+; TRAP-NEXT:    brk #0xc471
+; TRAP-NEXT:  Lauth_success_1:
+; TRAP-NEXT:    pacia x17, x3
+; TRAP-NEXT:    mov x0, x17
+; TRAP-NEXT:    ret
   %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 1, i64 
%arg1, i64 %arg2, i32 0, i64 %arg3)
   ret i64 %tmp
 }
 
 define i64 @test_resign_with_pc_ia_ib(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
-; CHECK-LABEL: test_resign_with_pc_ia_ib:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    mov x17, x0
-; CHECK-NEXT:    mov x16, x1
-; CHECK-NEXT:    mov x15, x2
-; CHECK-NEXT:    autia171615
-; CHECK-NEXT:    mov x0, x17
-; CHECK-NEXT:    pacib x0, x3
-; CHECK-NEXT:    ret
+; UNCHECKED-LABEL: test_resign_with_pc_ia_ib:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x17, x0
+; UNCHECKED-NEXT:    mov x16, x1
+; UNCHECKED-NEXT:    mov x15, x2
+; UNCHECKED-NEXT:    autia171615
+; UNCHECKED-NEXT:    pacib x17, x3
+; UNCHECKED-NEXT:    mov x0, x17
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_with_pc_ia_ib:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x17, x0
+; CHECKED-NEXT:    mov x16, x1
+; CHECKED-NEXT:    mov x15, x2
+; CHECKED-NEXT:    autia171615
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    xpaci x16
+; CHECKED-NEXT:    cmp x17, x16
+; CHECKED-NEXT:    b.eq Lauth_success_2
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    b Lresign_end_2
+; CHECKED-NEXT:  Lauth_success_2:
+; CHECKED-NEXT:    pacib x17, x3
+; CHECKED-NEXT:  Lresign_end_2:
+; CHECKED-NEXT:    mov x0, x17
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_with_pc_ia_ib:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x17, x0
+; TRAP-NEXT:    mov x16, x1
+; TRAP-NEXT:    mov x15, x2
+; TRAP-NEXT:    autia171615
+; TRAP-NEXT:    mov x16, x17
+; TRAP-NEXT:    xpaci x16
+; TRAP-NEXT:    cmp x17, x16
+; TRAP-NEXT:    b.eq Lauth_success_2
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_2:
+; TRAP-NEXT:    pacib x17, x3
+; TRAP-NEXT:    mov x0, x17
+; TRAP-NEXT:    ret
   %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
%arg1, i64 %arg2, i32 1, i64 %arg3)
   ret i64 %tmp
 }
 
 define i64 @test_resign_with_pc_ib_ib(i64 %arg, i64 %arg1, i64 %arg2, i64 
%arg3) {
-; CHECK-LABEL: test_resign_with_pc_ib_ib:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    mov x17, x0
-; CHECK-NEXT:    mov x16, x1
-; CHECK-NEXT:    mov x15, x2
-; CHECK-NEXT:    autib171615
-; CHECK-NEXT:    mov x0, x17
-; CHECK-NEXT:    pacib x0, x3
-; CHECK-NEXT:    ret
+; UNCHECKED-LABEL: test_resign_with_pc_ib_ib:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x17, x0
+; UNCHECKED-NEXT:    mov x16, x1
+; UNCHECKED-NEXT:    mov x15, x2
+; UNCHECKED-NEXT:    autib171615
+; UNCHECKED-NEXT:    pacib x17, x3
+; UNCHECKED-NEXT:    mov x0, x17
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_with_pc_ib_ib:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x17, x0
+; CHECKED-NEXT:    mov x16, x1
+; CHECKED-NEXT:    mov x15, x2
+; CHECKED-NEXT:    autib171615
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    xpaci x16
+; CHECKED-NEXT:    cmp x17, x16
+; CHECKED-NEXT:    b.eq Lauth_success_3
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    b Lresign_end_3
+; CHECKED-NEXT:  Lauth_success_3:
+; CHECKED-NEXT:    pacib x17, x3
+; CHECKED-NEXT:  Lresign_end_3:
+; CHECKED-NEXT:    mov x0, x17
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_with_pc_ib_ib:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x17, x0
+; TRAP-NEXT:    mov x16, x1
+; TRAP-NEXT:    mov x15, x2
+; TRAP-NEXT:    autib171615
+; TRAP-NEXT:    mov x16, x17
+; TRAP-NEXT:    xpaci x16
+; TRAP-NEXT:    cmp x17, x16
+; TRAP-NEXT:    b.eq Lauth_success_3
+; TRAP-NEXT:    brk #0xc471
+; TRAP-NEXT:  Lauth_success_3:
+; TRAP-NEXT:    pacib x17, x3
+; TRAP-NEXT:    mov x0, x17
+; TRAP-NEXT:    ret
   %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 1, i64 
%arg1, i64 %arg2, i32 1, i64 %arg3)
   ret i64 %tmp
 }
 
 define i64 @test_resign_with_pc_iza_ib(i64 %arg, i64 %arg1, i64 %arg2) {
-; CHECK-LABEL: test_resign_with_pc_iza_ib:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    mov x17, x0
-; CHECK-NEXT:    mov x16, #0 ; =0x0
-; CHECK-NEXT:    mov x15, x1
-; CHECK-NEXT:    autia171615
-; CHECK-NEXT:    mov x0, x17
-; CHECK-NEXT:    pacib x0, x2
-; CHECK-NEXT:    ret
+; UNCHECKED-LABEL: test_resign_with_pc_iza_ib:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x17, x0
+; UNCHECKED-NEXT:    mov x16, #0 ; =0x0
+; UNCHECKED-NEXT:    mov x15, x1
+; UNCHECKED-NEXT:    autia171615
+; UNCHECKED-NEXT:    pacib x17, x2
+; UNCHECKED-NEXT:    mov x0, x17
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_with_pc_iza_ib:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x17, x0
+; CHECKED-NEXT:    mov x16, #0 ; =0x0
+; CHECKED-NEXT:    mov x15, x1
+; CHECKED-NEXT:    autia171615
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    xpaci x16
+; CHECKED-NEXT:    cmp x17, x16
+; CHECKED-NEXT:    b.eq Lauth_success_4
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    b Lresign_end_4
+; CHECKED-NEXT:  Lauth_success_4:
+; CHECKED-NEXT:    pacib x17, x2
+; CHECKED-NEXT:  Lresign_end_4:
+; CHECKED-NEXT:    mov x0, x17
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_with_pc_iza_ib:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x17, x0
+; TRAP-NEXT:    mov x16, #0 ; =0x0
+; TRAP-NEXT:    mov x15, x1
+; TRAP-NEXT:    autia171615
+; TRAP-NEXT:    mov x16, x17
+; TRAP-NEXT:    xpaci x16
+; TRAP-NEXT:    cmp x17, x16
+; TRAP-NEXT:    b.eq Lauth_success_4
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_4:
+; TRAP-NEXT:    pacib x17, x2
+; TRAP-NEXT:    mov x0, x17
+; TRAP-NEXT:    ret
   %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
0, i64 %arg1, i32 1, i64 %arg2)
   ret i64 %tmp
 }
 
 define i64 @test_resign_with_pc_ia_izb(i64 %arg, i64 %arg1, i64 %arg2) {
-; CHECK-LABEL: test_resign_with_pc_ia_izb:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    mov x17, x0
-; CHECK-NEXT:    mov x16, x1
-; CHECK-NEXT:    mov x15, x2
-; CHECK-NEXT:    autia171615
-; CHECK-NEXT:    mov x0, x17
-; CHECK-NEXT:    pacizb x0
-; CHECK-NEXT:    ret
+; UNCHECKED-LABEL: test_resign_with_pc_ia_izb:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x17, x0
+; UNCHECKED-NEXT:    mov x16, x1
+; UNCHECKED-NEXT:    mov x15, x2
+; UNCHECKED-NEXT:    autia171615
+; UNCHECKED-NEXT:    pacizb x17
+; UNCHECKED-NEXT:    mov x0, x17
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_with_pc_ia_izb:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x17, x0
+; CHECKED-NEXT:    mov x16, x1
+; CHECKED-NEXT:    mov x15, x2
+; CHECKED-NEXT:    autia171615
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    xpaci x16
+; CHECKED-NEXT:    cmp x17, x16
+; CHECKED-NEXT:    b.eq Lauth_success_5
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    b Lresign_end_5
+; CHECKED-NEXT:  Lauth_success_5:
+; CHECKED-NEXT:    pacizb x17
+; CHECKED-NEXT:  Lresign_end_5:
+; CHECKED-NEXT:    mov x0, x17
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_with_pc_ia_izb:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x17, x0
+; TRAP-NEXT:    mov x16, x1
+; TRAP-NEXT:    mov x15, x2
+; TRAP-NEXT:    autia171615
+; TRAP-NEXT:    mov x16, x17
+; TRAP-NEXT:    xpaci x16
+; TRAP-NEXT:    cmp x17, x16
+; TRAP-NEXT:    b.eq Lauth_success_5
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_5:
+; TRAP-NEXT:    pacizb x17
+; TRAP-NEXT:    mov x0, x17
+; TRAP-NEXT:    ret
   %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
%arg1, i64 %arg2, i32 1, i64 0)
   ret i64 %tmp
 }
 
+; The "ptrauth-auth-traps" function attribute enables trapping in default
+; (checked) mode, with the same behavior as -aarch64-ptrauth-auth-checks=trap.
+define i64 @test_resign_with_pc_trap_attribute(i64 %arg, i64 %arg1, i64 %arg2, 
i64 %arg3) "ptrauth-auth-traps" {
+; UNCHECKED-LABEL: test_resign_with_pc_trap_attribute:
+; UNCHECKED:       ; %bb.0:
+; UNCHECKED-NEXT:    mov x17, x0
+; UNCHECKED-NEXT:    mov x16, x1
+; UNCHECKED-NEXT:    mov x15, x2
+; UNCHECKED-NEXT:    autia171615
+; UNCHECKED-NEXT:    pacia x17, x3
+; UNCHECKED-NEXT:    mov x0, x17
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_with_pc_trap_attribute:
+; CHECKED:       ; %bb.0:
+; CHECKED-NEXT:    mov x17, x0
+; CHECKED-NEXT:    mov x16, x1
+; CHECKED-NEXT:    mov x15, x2
+; CHECKED-NEXT:    autia171615
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    xpaci x16
+; CHECKED-NEXT:    cmp x17, x16
+; CHECKED-NEXT:    b.eq Lauth_success_6
+; CHECKED-NEXT:    brk #0xc470
+; CHECKED-NEXT:  Lauth_success_6:
+; CHECKED-NEXT:    pacia x17, x3
+; CHECKED-NEXT:    mov x0, x17
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_with_pc_trap_attribute:
+; TRAP:       ; %bb.0:
+; TRAP-NEXT:    mov x17, x0
+; TRAP-NEXT:    mov x16, x1
+; TRAP-NEXT:    mov x15, x2
+; TRAP-NEXT:    autia171615
+; TRAP-NEXT:    mov x16, x17
+; TRAP-NEXT:    xpaci x16
+; TRAP-NEXT:    cmp x17, x16
+; TRAP-NEXT:    b.eq Lauth_success_6
+; TRAP-NEXT:    brk #0xc470
+; TRAP-NEXT:  Lauth_success_6:
+; TRAP-NEXT:    pacia x17, x3
+; TRAP-NEXT:    mov x0, x17
+; TRAP-NEXT:    ret
+  %tmp = call i64 @llvm.ptrauth.auth.with.pc.and.resign(i64 %arg, i32 0, i64 
%arg1, i64 %arg2, i32 0, i64 %arg3)
+  ret i64 %tmp
+}
+
 declare i64 @llvm.ptrauth.auth.with.pc.and.resign(i64, i32, i64, i64, i32, i64)

>From 5aaa9ac28081e59789156506f5a566a62816c5d9 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Thu, 11 Jun 2026 15:17:04 -0700
Subject: [PATCH 6/9] clang-format

---
 llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp 
b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index b2deefd50e9e4..e1637e66e2ba5 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -1790,12 +1790,11 @@ void 
AArch64DAGToDAGISel::SelectPtrauthResignWithPC(SDNode *N) {
   SDValue X16Copy =
       CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X16,
                            AUTAddrDisc, X17Copy.getValue(1));
-  SDValue X15Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
-                                         AArch64::X15, AUTPC,
-                                         X16Copy.getValue(1));
+  SDValue X15Copy = CurDAG->getCopyToReg(
+      CurDAG->getEntryNode(), DL, AArch64::X15, AUTPC, X16Copy.getValue(1));
 
-  SDValue Ops[] = {AUTKey, AUTConstDisc, PACKey, PACConstDisc, PACAddrDisc,
-                   X15Copy.getValue(1)};
+  SDValue Ops[] = {AUTKey,       AUTConstDisc, PACKey,
+                   PACConstDisc, PACAddrDisc,  X15Copy.getValue(1)};
   SDNode *AUTPCPAC =
       CurDAG->getMachineNode(AArch64::AUTPCPAC, DL, MVT::i64, Ops);
   ReplaceNode(N, AUTPCPAC);

>From 5fcf19f7fceca8a06965cfc69adbe42f927b25aa Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Fri, 12 Jun 2026 09:31:38 -0700
Subject: [PATCH 7/9] no constant discriminator on aut

---
 llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 110 +++++++++++-------
 .../Target/AArch64/AArch64ISelDAGToDAG.cpp    |  19 ++-
 llvm/lib/Target/AArch64/AArch64InstrInfo.td   |  11 +-
 .../GISel/AArch64InstructionSelector.cpp      |  12 +-
 4 files changed, 79 insertions(+), 73 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp 
b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 47b57721bb635..3a84c275acce9 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -190,13 +190,10 @@ class AArch64AsmPrinter : public AsmPrinter {
   void emitPtrauthTailCallHardening(const MachineInstr *TC);
 
   struct PtrAuthSchema {
-    // Standard schema: explicit address-discriminator operand, no PC blending.
-    PtrAuthSchema(AArch64PACKey::ID Key, uint64_t IntDisc,
-                  const MachineOperand &AddrDiscOp);
-
-    // PC-blending schema (for auti[ab]171615): x16 is the implicit address
-    // discriminator; PCDisc (typically x15) carries the PC half.
-    PtrAuthSchema(AArch64PACKey::ID Key, uint64_t IntDisc, Register PCDisc);
+    static PtrAuthSchema CreateImmReg(AArch64PACKey::ID Key, uint64_t IntDisc,
+                                      const MachineOperand &AddrDiscOp);
+    static PtrAuthSchema CreateRegReg(AArch64PACKey::ID Key, Register AddrDisc,
+                                      Register PCDisc);
 
     AArch64PACKey::ID Key;
     uint64_t IntDisc;
@@ -2244,16 +2241,30 @@ bool 
AArch64AsmPrinter::emitDeactivationSymbolRelocation(Value *DS) {
   return false;
 }
 
-AArch64AsmPrinter::PtrAuthSchema::PtrAuthSchema(
-    AArch64PACKey::ID Key, uint64_t IntDisc, const MachineOperand &AddrDiscOp)
-    : Key(Key), IntDisc(IntDisc), AddrDisc(AddrDiscOp.getReg()),
-      AddrDiscIsKilled(AddrDiscOp.isKill()), PCDisc(AArch64::NoRegister) {}
+AArch64AsmPrinter::PtrAuthSchema 
AArch64AsmPrinter::PtrAuthSchema::CreateImmReg(
+    AArch64PACKey::ID Key, uint64_t IntDisc, const MachineOperand &AddrDiscOp) 
{
+  PtrAuthSchema Schema{
+      .Key = Key,
+      .IntDisc = IntDisc,
+      .AddrDisc = AddrDiscOp.getReg(),
+      .AddrDiscIsKilled = AddrDiscOp.isKill(),
+  };
+  return Schema;
+}
 
-AArch64AsmPrinter::PtrAuthSchema::PtrAuthSchema(AArch64PACKey::ID Key,
-                                                uint64_t IntDisc,
-                                                Register PCDisc)
-    : Key(Key), IntDisc(IntDisc), AddrDisc(AArch64::X16),
-      AddrDiscIsKilled(false), PCDisc(PCDisc) {}
+AArch64AsmPrinter::PtrAuthSchema 
AArch64AsmPrinter::PtrAuthSchema::CreateRegReg(
+    AArch64PACKey::ID Key, Register AddrDisc, Register PCDisc) {
+  assert(PCDisc != AArch64::NoRegister &&
+         "Use CreateImmReg for non-PC schemas");
+  PtrAuthSchema Schema{
+      .Key = Key,
+      .IntDisc = 0,
+      .AddrDisc = AddrDisc,
+      .AddrDiscIsKilled = true,
+      .PCDisc = PCDisc,
+  };
+  return Schema;
+}
 
 void AArch64AsmPrinter::emitPtrauthApplyIndirectAddend(Register Pointer,
                                                        Register Scratch,
@@ -2353,18 +2364,21 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(
   }
 
   if (WithPC) {
-    // auti[ab]171615: pointer is in x17, address discriminator in x16,
-    // PC discriminator in x15.
     assert(Pointer == AArch64::X17 && Scratch == AArch64::X16 &&
-           "AUTPCPAC must use x17/x16 as Pointer/Scratch");
+           "AUTx15x16x17PAC must use x17/x16 as Pointer/Scratch");
 
-    // Blend the constant discriminator into x16 in-place.
-    Register AUTDiscReg =
-        emitPtrauthDiscriminator(AuthSchema.IntDisc, Scratch, Scratch,
-                                 /*MayClobberAddrDisc=*/true);
-    assert((AUTDiscReg == Scratch || AUTDiscReg == AArch64::XZR) &&
-           "AUT discriminator must be in x16 for auti[ab]171615");
-    (void)AUTDiscReg;
+    assert(AuthSchema.AddrDisc == AArch64::X16 &&
+           "AUTx15x16x17PAC requires address discriminator in X16");
+
+    assert(AuthSchema.PCDisc == AArch64::X15 &&
+           "AUTx15x16x17PAC requires PC discriminator in X15");
+
+    assert(AuthSchema.IntDisc == 0 &&
+           "AUTx15x16x17PAC does not support IntDisc");
+
+    assert((AuthSchema.Key == AArch64PACKey::IB ||
+            AuthSchema.Key == AArch64PACKey::IA) &&
+           "AUTx15x16x17PAC only supports AUT-ing with IA/IB");
 
     if (!emitDeactivationSymbolRelocation(DS)) {
       unsigned AutOpc = (AuthSchema.Key == AArch64PACKey::IB)
@@ -3335,8 +3349,9 @@ void AArch64AsmPrinter::emitInstruction(const 
MachineInstr *MI) {
     const Register Pointer = AArch64::X16;
     const Register Scratch = AArch64::X17;
 
-    PtrAuthSchema AuthSchema((AArch64PACKey::ID)MI->getOperand(0).getImm(),
-                             MI->getOperand(1).getImm(), MI->getOperand(2));
+    auto AuthSchema = PtrAuthSchema::CreateImmReg(
+        (AArch64PACKey::ID)MI->getOperand(0).getImm(),
+        MI->getOperand(1).getImm(), MI->getOperand(2));
 
     emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, std::nullopt,
                           std::nullopt, MI->getDeactivationSymbol());
@@ -3347,8 +3362,9 @@ void AArch64AsmPrinter::emitInstruction(const 
MachineInstr *MI) {
     const Register Pointer = MI->getOperand(0).getReg();
     const Register Scratch = MI->getOperand(1).getReg();
 
-    PtrAuthSchema AuthSchema((AArch64PACKey::ID)MI->getOperand(3).getImm(),
-                             MI->getOperand(4).getImm(), MI->getOperand(5));
+    auto AuthSchema = PtrAuthSchema::CreateImmReg(
+        (AArch64PACKey::ID)MI->getOperand(3).getImm(),
+        MI->getOperand(4).getImm(), MI->getOperand(5));
 
     emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, std::nullopt,
                           std::nullopt, MI->getDeactivationSymbol());
@@ -3359,25 +3375,27 @@ void AArch64AsmPrinter::emitInstruction(const 
MachineInstr *MI) {
     const Register Pointer = AArch64::X16;
     const Register Scratch = AArch64::X17;
 
-    PtrAuthSchema AuthSchema((AArch64PACKey::ID)MI->getOperand(0).getImm(),
-                             MI->getOperand(1).getImm(), MI->getOperand(2));
+    auto AuthSchema = PtrAuthSchema::CreateImmReg(
+        (AArch64PACKey::ID)MI->getOperand(0).getImm(),
+        MI->getOperand(1).getImm(), MI->getOperand(2));
 
-    PtrAuthSchema SignSchema((AArch64PACKey::ID)MI->getOperand(3).getImm(),
-                             MI->getOperand(4).getImm(), MI->getOperand(5));
+    auto SignSchema = PtrAuthSchema::CreateImmReg(
+        (AArch64PACKey::ID)MI->getOperand(3).getImm(),
+        MI->getOperand(4).getImm(), MI->getOperand(5));
 
     emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, SignSchema,
                           std::nullopt, MI->getDeactivationSymbol());
     return;
   }
 
-  case AArch64::AUTPCPAC: {
-    // x17 = pointer, x16 = AUTAddrDisc, x15 = PC discriminator (all implicit).
-    PtrAuthSchema AuthSchema((AArch64PACKey::ID)MI->getOperand(0).getImm(),
-                             MI->getOperand(1).getImm(),
-                             /*PCDisc=*/AArch64::X15);
+  case AArch64::AUTx15x16x17PAC: {
+    auto AuthSchema = PtrAuthSchema::CreateRegReg(
+        (AArch64PACKey::ID)MI->getOperand(0).getImm(), AArch64::X16,
+        AArch64::X15);
 
-    PtrAuthSchema SignSchema((AArch64PACKey::ID)MI->getOperand(2).getImm(),
-                             MI->getOperand(3).getImm(), MI->getOperand(4));
+    auto SignSchema = PtrAuthSchema::CreateImmReg(
+        (AArch64PACKey::ID)MI->getOperand(1).getImm(),
+        MI->getOperand(2).getImm(), MI->getOperand(3));
 
     emitPtrauthAuthResign(/*Pointer=*/AArch64::X17, /*Scratch=*/AArch64::X16,
                           AuthSchema, SignSchema, std::nullopt,
@@ -3389,11 +3407,13 @@ void AArch64AsmPrinter::emitInstruction(const 
MachineInstr *MI) {
     const Register Pointer = AArch64::X16;
     const Register Scratch = AArch64::X17;
 
-    PtrAuthSchema AuthSchema((AArch64PACKey::ID)MI->getOperand(0).getImm(),
-                             MI->getOperand(1).getImm(), MI->getOperand(2));
+    auto AuthSchema = PtrAuthSchema::CreateImmReg(
+        (AArch64PACKey::ID)MI->getOperand(0).getImm(),
+        MI->getOperand(1).getImm(), MI->getOperand(2));
 
-    PtrAuthSchema SignSchema((AArch64PACKey::ID)MI->getOperand(3).getImm(),
-                             MI->getOperand(4).getImm(), MI->getOperand(5));
+    auto SignSchema = PtrAuthSchema::CreateImmReg(
+        (AArch64PACKey::ID)MI->getOperand(3).getImm(),
+        MI->getOperand(4).getImm(), MI->getOperand(5));
 
     emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, SignSchema,
                           MI->getOperand(6).getImm(),
diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp 
b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index e1637e66e2ba5..21e5077e315e5 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -1777,27 +1777,22 @@ void 
AArch64DAGToDAGISel::SelectPtrauthResignWithPC(SDNode *N) {
   AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64);
   PACKey = CurDAG->getTargetConstant(PACKeyC, DL, MVT::i64);
 
-  SDValue AUTAddrDisc, AUTConstDisc;
-  std::tie(AUTConstDisc, AUTAddrDisc) =
-      extractPtrauthBlendDiscriminators(AUTDisc, CurDAG);
-
   SDValue PACAddrDisc, PACConstDisc;
   std::tie(PACConstDisc, PACAddrDisc) =
       extractPtrauthBlendDiscriminators(PACDisc, CurDAG);
 
   SDValue X17Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL,
                                          AArch64::X17, Val, SDValue());
-  SDValue X16Copy =
-      CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X16,
-                           AUTAddrDisc, X17Copy.getValue(1));
+  SDValue X16Copy = CurDAG->getCopyToReg(
+      CurDAG->getEntryNode(), DL, AArch64::X16, AUTDisc, X17Copy.getValue(1));
   SDValue X15Copy = CurDAG->getCopyToReg(
       CurDAG->getEntryNode(), DL, AArch64::X15, AUTPC, X16Copy.getValue(1));
 
-  SDValue Ops[] = {AUTKey,       AUTConstDisc, PACKey,
-                   PACConstDisc, PACAddrDisc,  X15Copy.getValue(1)};
-  SDNode *AUTPCPAC =
-      CurDAG->getMachineNode(AArch64::AUTPCPAC, DL, MVT::i64, Ops);
-  ReplaceNode(N, AUTPCPAC);
+  SDValue Ops[] = {AUTKey, PACKey, PACConstDisc, PACAddrDisc,
+                   X15Copy.getValue(1)};
+  SDNode *AUTx15x16x17PAC =
+      CurDAG->getMachineNode(AArch64::AUTx15x16x17PAC, DL, MVT::i64, Ops);
+  ReplaceNode(N, AUTx15x16x17PAC);
 }
 
 bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) {
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td 
b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index ceb288e1e0e49..21184132b55b5 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -2277,13 +2277,12 @@ let Predicates = [HasPAuth] in {
     let Uses = [X16];
   }
 
-  // AUT and re-PAC a value using different keys/data, with a PC value blended
-  // into the AUT'd discriminator.
-  // This directly manupulates x15,x16,x17, which are the only registers that
-  // certain OSs guarantee are safe to use for sensitive operations.
-  def AUTPCPAC
+  // AUT (using x15=PC, x16=addr disc, x17=pointer) and re-PAC a value using
+  // different keys/data. This directly manipulates x15/x16/x17, which are the
+  // only registers certain OSs guarantee are safe to use for sensitive ops.
+  def AUTx15x16x17PAC
       : Pseudo<(outs),
-               (ins i32imm:$AUTKey, i64imm:$AUTDisc,
+               (ins i32imm:$AUTKey,
                     i32imm:$PACKey, i64imm:$PACDisc, GPR64noip:$PACAddrDisc),
                []>, Sched<[WriteI, ReadI]> {
     let isCodeGenOnly = 1;
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp 
b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index eea2e560aa68e..080045265b9ae 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -6706,28 +6706,20 @@ bool 
AArch64InstructionSelector::selectIntrinsic(MachineInstr &I,
     assert((AUTKey == AArch64PACKey::IA || AUTKey == AArch64PACKey::IB) &&
            "auth_with_pc_and_resign only supports IA and IB keys");
 
-    uint16_t AUTConstDiscC = 0;
-    Register AUTAddrDisc;
-    std::tie(AUTConstDiscC, AUTAddrDisc) =
-        extractPtrauthBlendDiscriminators(AUTDisc, MRI);
-
     uint16_t PACConstDiscC = 0;
     Register PACAddrDisc;
     std::tie(PACConstDiscC, PACAddrDisc) =
         extractPtrauthBlendDiscriminators(PACDisc, MRI);
 
-    if (AUTAddrDisc == AArch64::NoRegister)
-      AUTAddrDisc = AArch64::XZR;
     if (PACAddrDisc == AArch64::NoRegister)
       PACAddrDisc = AArch64::XZR;
 
     MIB.buildCopy({AArch64::X17}, {ValReg});
-    MIB.buildCopy({AArch64::X16}, {AUTAddrDisc});
+    MIB.buildCopy({AArch64::X16}, {AUTDisc});
     MIB.buildCopy({AArch64::X15}, {AUTPC});
 
-    MIB.buildInstr(AArch64::AUTPCPAC)
+    MIB.buildInstr(AArch64::AUTx15x16x17PAC)
         .addImm(AUTKey)
-        .addImm(AUTConstDiscC)
         .addImm(PACKey)
         .addImm(PACConstDiscC)
         .addUse(PACAddrDisc)

>From ca4fec9294d4025eaf055a91cfd5340c294dc35c Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Fri, 12 Jun 2026 10:32:56 -0700
Subject: [PATCH 8/9] no designated init

---
 llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 25 +++++++++----------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp 
b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 3a84c275acce9..fdbd04a8553a5 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -2243,12 +2243,12 @@ bool 
AArch64AsmPrinter::emitDeactivationSymbolRelocation(Value *DS) {
 
 AArch64AsmPrinter::PtrAuthSchema 
AArch64AsmPrinter::PtrAuthSchema::CreateImmReg(
     AArch64PACKey::ID Key, uint64_t IntDisc, const MachineOperand &AddrDiscOp) 
{
-  PtrAuthSchema Schema{
-      .Key = Key,
-      .IntDisc = IntDisc,
-      .AddrDisc = AddrDiscOp.getReg(),
-      .AddrDiscIsKilled = AddrDiscOp.isKill(),
-  };
+  PtrAuthSchema Schema;
+  Schema.Key = Key;
+  Schema.IntDisc = IntDisc;
+  Schema.AddrDisc = AddrDiscOp.getReg();
+  Schema.AddrDiscIsKilled = AddrDiscOp.isKill();
+  Schema.PCDisc = AArch64::NoRegister;
   return Schema;
 }
 
@@ -2256,13 +2256,12 @@ AArch64AsmPrinter::PtrAuthSchema 
AArch64AsmPrinter::PtrAuthSchema::CreateRegReg(
     AArch64PACKey::ID Key, Register AddrDisc, Register PCDisc) {
   assert(PCDisc != AArch64::NoRegister &&
          "Use CreateImmReg for non-PC schemas");
-  PtrAuthSchema Schema{
-      .Key = Key,
-      .IntDisc = 0,
-      .AddrDisc = AddrDisc,
-      .AddrDiscIsKilled = true,
-      .PCDisc = PCDisc,
-  };
+  PtrAuthSchema Schema;
+  Schema.Key = Key;
+  Schema.IntDisc = 0;
+  Schema.AddrDisc = AddrDisc;
+  Schema.AddrDiscIsKilled = true;
+  Schema.PCDisc = PCDisc;
   return Schema;
 }
 

>From 1159926ea207f5f5a468944be139a757f38a662c Mon Sep 17 00:00:00 2001
From: Jon Roelofs <[email protected]>
Date: Mon, 15 Jun 2026 10:48:17 -0700
Subject: [PATCH 9/9] fix docs build

---
 clang/docs/PointerAuthentication.rst | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/docs/PointerAuthentication.rst 
b/clang/docs/PointerAuthentication.rst
index 9f0a30caf5343..01fba5f4352f0 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -666,6 +666,8 @@ same behavior on a null pointer that the language 
implementation would.
 This is a treacherous operation that can easily result in `signing oracles`_.
 Programs should use it seldom and carefully.
 
+.. _ptrauth_auth_and_resign:
+
 ``ptrauth_auth_and_resign``
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -688,6 +690,7 @@ to guarantee non-attackability if these expressions are
 :ref:`safely-derived<Safe derivation>`.
 
 ``ptrauth_auth_with_pc_and_resign``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. code-block:: c
 

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

Reply via email to