[llvm] [AArch64][PAC] Mark $Scratch operand of AUTxMxN as earlyclobber (PR #173999)

Anatoly Trosinenko via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 29 07:55:36 PST 2026


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

>From 007fccabe8deee634e2ddc18eafb544f6b0621bb Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 30 Dec 2025 18:29:19 +0300
Subject: [PATCH 1/3] [AArch64][PAC] Mark $Scratch operand of AUTxMxN as
 earlyclobber

This fixes an assertions when emitting code at `-O0`.
---
 llvm/lib/Target/AArch64/AArch64InstrInfo.td | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index f3cd613a6bd99..5ad0f92b0dfd7 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -2195,10 +2195,21 @@ let Predicates = [HasPAuth] in {
     let Uses = [X16];
   }
 
+  // AsmPrinter can clobber the $AddrDisc register as long as it is marked
+  // "killed", otherwise an assertion checks that $Scratch != $AddrDisc.
+  // The problem is that it is always correct to *omit* the "killed" flag,
+  // thus an instruction like this can be passed to AsmPrinter:
+  //
+  //     $x8, $x9 = AUTxMxN $x8, 0, 51756, $x9, implicit-def $nzcv
+  //
+  // While it is possible to check if "$Scratch == $AddrDisc OR $AddrDisc
+  // is killed" instead, it is easier and more straightforward to mark
+  // $Scratch as @earlyclobber. Furthermore, this would be mandatory for a
+  // (hypotetical at the time of writing this comment) AUTPACxMxN pseudo.
   def AUTxMxN : Pseudo<(outs GPR64:$AuthVal, GPR64common:$Scratch),
                        (ins GPR64:$Val, i32imm:$Key,
                             i64imm:$Disc, GPR64:$AddrDisc),
-                       [], "$AuthVal = $Val">, Sched<[WriteI, ReadI]> {
+                       [], "$AuthVal = $Val, at earlyclobber $Scratch">, Sched<[WriteI, ReadI]> {
     let isCodeGenOnly = 1;
     let hasSideEffects = 1;
     let mayStore = 0;

>From f6a86957d8bbe707dd9ca393d1274fde92b0a139 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 8 Jan 2026 15:36:25 +0300
Subject: [PATCH 2/3] Improve the comment wording a bit

---
 llvm/lib/Target/AArch64/AArch64InstrInfo.td | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 5ad0f92b0dfd7..5acbfb8971079 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -2195,17 +2195,17 @@ let Predicates = [HasPAuth] in {
     let Uses = [X16];
   }
 
-  // AsmPrinter can clobber the $AddrDisc register as long as it is marked
-  // "killed", otherwise an assertion checks that $Scratch != $AddrDisc.
-  // The problem is that it is always correct to *omit* the "killed" flag,
-  // thus an instruction like this can be passed to AsmPrinter:
+  // AArch64AsmPrinter can clobber the $AddrDisc register as long as it is
+  // marked "killed", otherwise an assertion in emitPtrauthDiscriminator checks
+  // that $Scratch != $AddrDisc. The problem is that it is always correct to
+  // *omit* the "killed" flag, thus an instruction like this is valid:
   //
-  //     $x8, $x9 = AUTxMxN $x8, 0, 51756, $x9, implicit-def $nzcv
+  //     $x8, $x9 = AUTxMxN $x8, 0, 12345, /* is actually killed */ $x9, implicit-def $nzcv
+  //     // $x9 is dead past this point
   //
-  // While it is possible to check if "$Scratch == $AddrDisc OR $AddrDisc
-  // is killed" instead, it is easier and more straightforward to mark
-  // $Scratch as @earlyclobber. Furthermore, this would be mandatory for a
-  // (hypotetical at the time of writing this comment) AUTPACxMxN pseudo.
+  // While it is possible to allow emitPtrauthDiscriminator to clobber $Scratch
+  // iff ($AddrDisc is killed OR $Scratch == $AddrDisc), it is easier and more
+  // straightforward to mark $Scratch as @earlyclobber.
   def AUTxMxN : Pseudo<(outs GPR64:$AuthVal, GPR64common:$Scratch),
                        (ins GPR64:$Val, i32imm:$Key,
                             i64imm:$Disc, GPR64:$AddrDisc),

>From 1b35218f935a9b31d92700ecf9ae3d1cccc32a12 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 29 Jan 2026 18:48:33 +0300
Subject: [PATCH 3/3] Test case: check generated MIR output

---
 llvm/test/CodeGen/AArch64/ptrauth-isel.ll | 61 +++++++++++++++++++++--
 1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/ptrauth-isel.ll b/llvm/test/CodeGen/AArch64/ptrauth-isel.ll
index 7011b946aad74..6ecfb5852f790 100644
--- a/llvm/test/CodeGen/AArch64/ptrauth-isel.ll
+++ b/llvm/test/CodeGen/AArch64/ptrauth-isel.ll
@@ -1,12 +1,12 @@
 ; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
 ; RUN: llc < %s -mtriple arm64e-apple-darwin             -verify-machineinstrs -stop-after=finalize-isel -global-isel=0 \
-; RUN:     | FileCheck %s --check-prefixes=DAGISEL
+; RUN:     | FileCheck %s --check-prefixes=DAGISEL,DAGISEL-DARWIN
 ; RUN: llc < %s -mtriple arm64e-apple-darwin             -verify-machineinstrs -stop-after=finalize-isel -global-isel=1 -global-isel-abort=1 \
-; RUN:     | FileCheck %s --check-prefixes=GISEL
+; RUN:     | FileCheck %s --check-prefixes=GISEL,GISEL-DARWIN
 ; RUN: llc < %s -mtriple aarch64-linux-gnu -mattr=+pauth -verify-machineinstrs -stop-after=finalize-isel -global-isel=0 \
-; RUN:     | FileCheck %s --check-prefixes=DAGISEL
+; RUN:     | FileCheck %s --check-prefixes=DAGISEL,DAGISEL-GNU
 ; RUN: llc < %s -mtriple aarch64-linux-gnu -mattr=+pauth -verify-machineinstrs -stop-after=finalize-isel -global-isel=1 -global-isel-abort=1 \
-; RUN:     | FileCheck %s --check-prefixes=GISEL
+; RUN:     | FileCheck %s --check-prefixes=GISEL,GISEL-GNU
 
 ; Check MIR produced by the instruction selector to validate properties that
 ; cannot be reliably tested by only inspecting the final asm output.
@@ -267,3 +267,56 @@ exit:
   %signed = call i64 @llvm.ptrauth.sign(i64 %addr, i32 2, i64 %disc)
   ret i64 %signed
 }
+
+; On non-Darwin platforms, AUTxMxN allows allocating arbitrary scratch registers.
+; To simplify expansion of AUTxMxN in AArch64AsmPrinter, $Scratch operand
+; should be marked as @earlyclobber, which is checked by this test case.
+define i64 @autxmxn_earlyclobbered_scratch(i64 %addr, i64 %disc) {
+  ; DAGISEL-DARWIN-LABEL: name: autxmxn_earlyclobbered_scratch
+  ; DAGISEL-DARWIN: bb.0.entry:
+  ; DAGISEL-DARWIN-NEXT:   liveins: $x0, $x1
+  ; DAGISEL-DARWIN-NEXT: {{  $}}
+  ; DAGISEL-DARWIN-NEXT:   [[COPY:%[0-9]+]]:gpr64noip = COPY $x1
+  ; DAGISEL-DARWIN-NEXT:   [[COPY1:%[0-9]+]]:gpr64 = COPY $x0
+  ; DAGISEL-DARWIN-NEXT:   $x16 = COPY [[COPY1]]
+  ; DAGISEL-DARWIN-NEXT:   AUTx16x17 2, 0, [[COPY]], implicit-def $x16, implicit-def dead $x17, implicit-def dead $nzcv, implicit $x16
+  ; DAGISEL-DARWIN-NEXT:   [[COPY2:%[0-9]+]]:gpr64all = COPY $x16
+  ; DAGISEL-DARWIN-NEXT:   $x0 = COPY [[COPY2]]
+  ; DAGISEL-DARWIN-NEXT:   RET_ReallyLR implicit $x0
+  ;
+  ; GISEL-DARWIN-LABEL: name: autxmxn_earlyclobbered_scratch
+  ; GISEL-DARWIN: bb.1.entry:
+  ; GISEL-DARWIN-NEXT:   liveins: $x0, $x1
+  ; GISEL-DARWIN-NEXT: {{  $}}
+  ; GISEL-DARWIN-NEXT:   [[COPY:%[0-9]+]]:gpr64all = COPY $x0
+  ; GISEL-DARWIN-NEXT:   [[COPY1:%[0-9]+]]:gpr64noip = COPY $x1
+  ; GISEL-DARWIN-NEXT:   $x16 = COPY [[COPY]]
+  ; GISEL-DARWIN-NEXT:   $x17 = IMPLICIT_DEF
+  ; GISEL-DARWIN-NEXT:   AUTx16x17 2, 0, [[COPY1]], implicit-def $x16, implicit-def $x17, implicit-def dead $nzcv, implicit $x16
+  ; GISEL-DARWIN-NEXT:   [[COPY2:%[0-9]+]]:gpr64 = COPY $x16
+  ; GISEL-DARWIN-NEXT:   $x0 = COPY [[COPY2]]
+  ; GISEL-DARWIN-NEXT:   RET_ReallyLR implicit $x0
+  ;
+  ; DAGISEL-GNU-LABEL: name: autxmxn_earlyclobbered_scratch
+  ; DAGISEL-GNU: bb.0.entry:
+  ; DAGISEL-GNU-NEXT:   liveins: $x0, $x1
+  ; DAGISEL-GNU-NEXT: {{  $}}
+  ; DAGISEL-GNU-NEXT:   [[COPY:%[0-9]+]]:gpr64 = COPY $x1
+  ; DAGISEL-GNU-NEXT:   [[COPY1:%[0-9]+]]:gpr64 = COPY $x0
+  ; DAGISEL-GNU-NEXT:   %2:gpr64, early-clobber %3:gpr64common = AUTxMxN [[COPY1]], 2, 0, [[COPY]], implicit-def dead $nzcv
+  ; DAGISEL-GNU-NEXT:   $x0 = COPY %2
+  ; DAGISEL-GNU-NEXT:   RET_ReallyLR implicit $x0
+  ;
+  ; GISEL-GNU-LABEL: name: autxmxn_earlyclobbered_scratch
+  ; GISEL-GNU: bb.1.entry:
+  ; GISEL-GNU-NEXT:   liveins: $x0, $x1
+  ; GISEL-GNU-NEXT: {{  $}}
+  ; GISEL-GNU-NEXT:   [[COPY:%[0-9]+]]:gpr64 = COPY $x0
+  ; GISEL-GNU-NEXT:   [[COPY1:%[0-9]+]]:gpr64 = COPY $x1
+  ; GISEL-GNU-NEXT:   %2:gpr64, early-clobber %3:gpr64common = AUTxMxN [[COPY]], 2, 0, [[COPY1]], implicit-def dead $nzcv
+  ; GISEL-GNU-NEXT:   $x0 = COPY %2
+  ; GISEL-GNU-NEXT:   RET_ReallyLR implicit $x0
+entry:
+  %auted = call i64 @llvm.ptrauth.auth(i64 %addr, i32 2, i64 %disc)
+  ret i64 %auted
+}



More information about the llvm-commits mailing list