[llvm-branch-commits] [llvm] [AArch64][PAC] Rework the expansion of AUT/AUTPAC pseudos (PR #169699)

Anatoly Trosinenko via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Dec 30 07:52:04 PST 2025


https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/169699

>From 1ccabb1524d32dbed840cb31348e997eac24f35e Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 25 Sep 2025 22:28:14 +0300
Subject: [PATCH] [AArch64][PAC] Rework the expansion of AUT/AUTPAC pseudos

Refactor `AArch64AsmPrinter::emitPtrauthAuthResign` to improve
readability and fix the conditions when `emitPtrauthDiscriminator` is
allowed to clobber AddrDisc.

* do not clobber `AUTAddrDisc` when computing `AUTDiscReg` on resigning
  if `AUTAddrDisc == PACAddrDisc`, as it would prevent passing raw,
  64-bit value as the new discriminator
* move the code computing `ShouldCheck` and `ShouldTrap` conditions to a
  separate function
---
 llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 103 +++++++++++-------
 ...trauth-intrinsic-auth-resign-with-blend.ll |  77 +++++++++++--
 2 files changed, 129 insertions(+), 51 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 2e83e60178a73..3bff383ee765f 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -2223,21 +2223,9 @@ AArch64AsmPrinter::PtrAuthSchema::PtrAuthSchema(
     : Key(Key), IntDisc(IntDisc), AddrDisc(AddrDiscOp.getReg()),
       AddrDiscIsKilled(AddrDiscOp.isKill()) {}
 
-void AArch64AsmPrinter::emitPtrauthAuthResign(
-    Register Pointer, Register Scratch, PtrAuthSchema AuthSchema,
-    std::optional<PtrAuthSchema> SignSchema, Value *DS) {
-  const bool IsResign = SignSchema.has_value();
-
-  // We expand AUT/AUTPAC into a sequence of the form
-  //
-  //      ; authenticate x16
-  //      ; check pointer in x16
-  //    Lsuccess:
-  //      ; sign x16 (if AUTPAC)
-  //    Lend:   ; if not trapping on failure
-  //
-  // with the checking sequence chosen depending on whether/how we should check
-  // the pointer and whether we should trap on failure.
+static std::pair<bool, bool> getCheckAndTrapMode(const MachineFunction *MF,
+                                                 bool IsResign) {
+  const AArch64Subtarget &STI = MF->getSubtarget<AArch64Subtarget>();
 
   // By default, auth/resign sequences check for auth failures.
   bool ShouldCheck = true;
@@ -2246,7 +2234,7 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(
 
   // On an FPAC CPU, you get traps whether you want them or not: there's
   // no point in emitting checks or traps.
-  if (STI->hasFPAC())
+  if (STI.hasFPAC())
     ShouldCheck = ShouldTrap = false;
 
   // However, command-line flags can override this, for experimentation.
@@ -2265,43 +2253,76 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(
     break;
   }
 
-  // Compute aut discriminator
-  Register AUTDiscReg =
-      emitPtrauthDiscriminator(AuthSchema.IntDisc, AuthSchema.AddrDisc, Scratch,
-                               AuthSchema.AddrDiscIsKilled);
+  // Checked-but-not-trapping mode ("poison") only applies to resigning,
+  // replace with "unchecked" for standalone AUT.
+  if (!IsResign && ShouldCheck && !ShouldTrap)
+    ShouldCheck = ShouldTrap = false;
 
-  if (!emitDeactivationSymbolRelocation(DS))
-    emitAUT(AuthSchema.Key, Pointer, AUTDiscReg);
+  return std::make_pair(ShouldCheck, ShouldTrap);
+}
 
-  // Unchecked or checked-but-non-trapping AUT is just an "AUT": we're done.
-  if (!IsResign && (!ShouldCheck || !ShouldTrap))
-    return;
+// We expand AUTx16x17/AUTxMxN into a sequence of the form
+//
+//      ; authenticate Pointer
+//      ; check that Pointer is valid (optional, traps on failure)
+//
+// We expand AUTPAC into a sequence of the form
+//
+//      ; authenticate Pointer
+//      ; check that Pointer is valid (optional, traps on failure)
+//      ; sign Pointer
+//
+// or
+//
+//      ; authenticate Pointer
+//      ; check that Pointer is valid (skips re-sign on failure)
+//      ; sign Pointer
+//    Lon_failure:
+//
+void AArch64AsmPrinter::emitPtrauthAuthResign(
+    Register Pointer, Register Scratch, PtrAuthSchema AuthSchema,
+    std::optional<PtrAuthSchema> SignSchema, Value *DS) {
+  const bool IsResign = SignSchema.has_value();
+
+  const auto [ShouldCheck, ShouldTrap] = getCheckAndTrapMode(MF, IsResign);
+  const bool ShouldSkipSignOnAuthFailure = ShouldCheck && !ShouldTrap;
+  assert((ShouldCheck || !ShouldTrap) && "ShouldTrap implies ShouldCheck");
+
+  // It is hardly meaningful to authenticate or sign a pointer using its own
+  // value, thus we only have to take care not to early-clobber
+  // AuthSchema.AddrDisc that is aliased with SignSchema->AddrDisc.
+  assert(Pointer != AuthSchema.AddrDisc);
+  assert(!SignSchema || Pointer != SignSchema->AddrDisc);
+  bool IsResignWithAliasedAddrDiscs =
+      IsResign && AuthSchema.AddrDisc == SignSchema->AddrDisc;
+  bool MayReuseAUTAddrDisc =
+      !IsResignWithAliasedAddrDiscs && AuthSchema.AddrDiscIsKilled;
+  Register AUTDiscReg = emitPtrauthDiscriminator(
+      AuthSchema.IntDisc, AuthSchema.AddrDisc, Scratch, MayReuseAUTAddrDisc);
 
-  MCSymbol *EndSym = nullptr;
+  if (!emitDeactivationSymbolRelocation(DS))
+    emitAUT(AuthSchema.Key, Pointer, AUTDiscReg);
 
-  if (ShouldCheck) {
-    if (IsResign && !ShouldTrap)
-      EndSym = createTempSymbol("resign_end_");
+  MCSymbol *OnFailure =
+      ShouldSkipSignOnAuthFailure ? createTempSymbol("resign_end_") : nullptr;
 
+  if (ShouldCheck)
     emitPtrauthCheckAuthenticatedValue(Pointer, Scratch, AuthSchema.Key,
                                        AArch64PAuth::AuthCheckMethod::XPAC,
-                                       EndSym);
-  }
+                                       OnFailure);
 
-  // We already emitted unchecked and checked-but-non-trapping AUTs.
-  // That left us with trapping AUTs, and AUTPACs.
-  // Trapping AUTs don't need PAC: we're done.
-  if (!IsResign)
+  if (!IsResign) {
+    assert(!OnFailure && "Poison mode only applies to resigning");
     return;
+  }
 
-  // Compute pac discriminator
-  Register PACDiscReg = emitPtrauthDiscriminator(SignSchema->IntDisc,
-                                                 SignSchema->AddrDisc, Scratch);
+  Register PACDiscReg =
+      emitPtrauthDiscriminator(SignSchema->IntDisc, SignSchema->AddrDisc,
+                               Scratch, SignSchema->AddrDiscIsKilled);
   emitPAC(SignSchema->Key, Pointer, PACDiscReg);
 
-  //  Lend:
-  if (EndSym)
-    OutStreamer->emitLabel(EndSym);
+  if (OnFailure)
+    OutStreamer->emitLabel(OnFailure);
 }
 
 void AArch64AsmPrinter::emitPtrauthSign(const MachineInstr *MI) {
diff --git a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll
index e2aea6df78250..ae5fa509d439b 100644
--- a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll
+++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll
@@ -1,4 +1,3 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0                    -verify-machineinstrs \
 ; RUN:   -aarch64-ptrauth-auth-checks=none | FileCheck %s -DL="L" --check-prefixes=UNCHECKED,UNCHECKED-DARWIN
 ; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \
@@ -84,12 +83,14 @@ define i64 @test_resign_blend(i64 %arg, i64 %arg1, i64 %arg2) {
 ; UNCHECKED-NEXT:    mov x16, x0
 ; UNCHECKED-ELF-NEXT:    movk x1, #12345, lsl #48
 ; UNCHECKED-ELF-NEXT:    autda x16, x1
+; UNCHECKED-ELF-NEXT:    movk x2, #56789, lsl #48
+; UNCHECKED-ELF-NEXT:    pacdb x16, x2
 ; UNCHECKED-DARWIN-NEXT: mov x17, x1
 ; UNCHECKED-DARWIN-NEXT: movk x17, #12345, lsl #48
 ; UNCHECKED-DARWIN-NEXT: autda x16, x17
-; UNCHECKED-NEXT:    mov x17, x2
-; UNCHECKED-NEXT:    movk x17, #56789, lsl #48
-; UNCHECKED-NEXT:    pacdb x16, x17
+; UNCHECKED-DARWIN-NEXT: mov x17, x2
+; UNCHECKED-DARWIN-NEXT: movk x17, #56789, lsl #48
+; UNCHECKED-DARWIN-NEXT: pacdb x16, x17
 ; UNCHECKED-NEXT:    mov x0, x16
 ; UNCHECKED-NEXT:    ret
 ;
@@ -108,9 +109,11 @@ define i64 @test_resign_blend(i64 %arg, i64 %arg1, i64 %arg2) {
 ; CHECKED-NEXT:    mov x16, x17
 ; CHECKED-NEXT:    b [[L]]resign_end_0
 ; CHECKED-NEXT:  Lauth_success_0:
-; CHECKED-NEXT:    mov x17, x2
-; CHECKED-NEXT:    movk x17, #56789, lsl #48
-; CHECKED-NEXT:    pacdb x16, x17
+; CHECKED-ELF-NEXT:    movk x2, #56789, lsl #48
+; CHECKED-ELF-NEXT:    pacdb x16, x2
+; CHECKED-DARWIN-NEXT: mov x17, x2
+; CHECKED-DARWIN-NEXT: movk x17, #56789, lsl #48
+; CHECKED-DARWIN-NEXT: pacdb x16, x17
 ; CHECKED-NEXT:  Lresign_end_0:
 ; CHECKED-NEXT:    mov x0, x16
 ; CHECKED-NEXT:    ret
@@ -129,9 +132,11 @@ define i64 @test_resign_blend(i64 %arg, i64 %arg1, i64 %arg2) {
 ; TRAP-NEXT:    b.eq [[L]]auth_success_1
 ; TRAP-NEXT:    brk #0xc472
 ; TRAP-NEXT:  Lauth_success_1:
-; TRAP-NEXT:    mov x17, x2
-; TRAP-NEXT:    movk x17, #56789, lsl #48
-; TRAP-NEXT:    pacdb x16, x17
+; TRAP-ELF-NEXT:    movk x2, #56789, lsl #48
+; TRAP-ELF-NEXT:    pacdb x16, x2
+; TRAP-DARWIN-NEXT: mov x17, x2
+; TRAP-DARWIN-NEXT: movk x17, #56789, lsl #48
+; TRAP-DARWIN-NEXT: pacdb x16, x17
 ; TRAP-NEXT:    mov x0, x16
 ; TRAP-NEXT:    ret
   %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 12345)
@@ -299,6 +304,58 @@ define i64 @test_auth_too_large_discriminator(i64 %arg, i64 %arg1) {
   ret i64 %tmp1
 }
 
+; As long as we support raw, non-blended 64-bit discriminators (which might be
+; useful for low-level code such as dynamic loaders), the "auth" part of resign
+; must not clobber %arg, if its upper bits are later used by the "sign" part.
+define i64 @test_resign_aliased_discs_raw_sign_disc(i64 %p, i64 %arg) {
+; UNCHECKED-LABEL: test_resign_aliased_discs_raw_sign_disc:
+; UNCHECKED:       %bb.0:
+; UNCHECKED-NEXT:    mov x16, x0
+; UNCHECKED-NEXT:    mov  x17, x1
+; UNCHECKED-NEXT:    movk x17, #12345, lsl #48
+; UNCHECKED-NEXT:    autda x16, x17
+; UNCHECKED-NEXT:    pacdb x16, x1
+; UNCHECKED-NEXT:    mov x0, x16
+; UNCHECKED-NEXT:    ret
+;
+; CHECKED-LABEL: test_resign_aliased_discs_raw_sign_disc:
+; CHECKED:       %bb.0:
+; CHECKED-NEXT:    mov x16, x0
+; CHECKED-NEXT:    mov  x17, x1
+; CHECKED-NEXT:    movk x17, #12345, lsl #48
+; CHECKED-NEXT:    autda x16, x17
+; CHECKED-NEXT:    mov x17, x16
+; CHECKED-NEXT:    xpacd x17
+; CHECKED-NEXT:    cmp x16, x17
+; CHECKED-NEXT:    b.eq [[L]]auth_success_3
+; CHECKED-NEXT:    mov x16, x17
+; CHECKED-NEXT:    b [[L]]resign_end_3
+; CHECKED-NEXT:  Lauth_success_3:
+; CHECKED-NEXT:    pacdb x16, x1
+; CHECKED-NEXT:  Lresign_end_3:
+; CHECKED-NEXT:    mov x0, x16
+; CHECKED-NEXT:    ret
+;
+; TRAP-LABEL: test_resign_aliased_discs_raw_sign_disc:
+; TRAP:       %bb.0:
+; TRAP-NEXT:    mov x16, x0
+; TRAP-NEXT:    mov  x17, x1
+; TRAP-NEXT:    movk x17, #12345, lsl #48
+; TRAP-NEXT:    autda x16, x17
+; TRAP-NEXT:    mov x17, x16
+; TRAP-NEXT:    xpacd x17
+; TRAP-NEXT:    cmp x16, x17
+; TRAP-NEXT:    b.eq [[L]]auth_success_5
+; TRAP-NEXT:    brk #0xc472
+; TRAP-NEXT:  Lauth_success_5:
+; TRAP-NEXT:    pacdb x16, x1
+; TRAP-NEXT:    mov x0, x16
+; TRAP-NEXT:    ret
+  %auth.disc = call i64 @llvm.ptrauth.blend(i64 %arg, i64 12345)
+  %res = call i64 @llvm.ptrauth.resign(i64 %p, i32 2, i64 %auth.disc, i32 3, i64 %arg)
+  ret i64 %res
+}
+
 declare i64 @llvm.ptrauth.auth(i64, i32, i64)
 declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64)
 declare i64 @llvm.ptrauth.blend(i64, i64)



More information about the llvm-branch-commits mailing list