[llvm-branch-commits] [llvm] [AArch64] Mark X16 as clobbered in PAUTH_EPILOGUE for hint-based PAuthLR (PR #175991)
Victor Campos via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jan 14 08:51:24 PST 2026
https://github.com/vhscampos created https://github.com/llvm/llvm-project/pull/175991
When users request branch protection with PAuthLR on targets that do not support the PAuthLR instructions, the PAUTH_EPILOGUE falls back to using hint-space instructions. This fallback sequence uses X16 as a temporary register, but X16 was not listed in the clobber set.
Because Speculative Load Hardening uses X16, this omission made SLH incompatible with this PAUTH_EPILOGUE path.
Mark X16 as clobbered so the compiler does not assume X16 is preserved across the epilogue, restoring compatibility with Speculative Load Hardening and avoiding incorrect register liveness assumptions. The clobber is added in C++ rather than TableGen, as X16 is only clobbered when PAuthLR is requested as a branch protection variation and should not be treated as clobbered unconditionally.
>From 5a4eee36937597c8191037e43042ee37b42405da Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Tue, 13 Jan 2026 20:26:30 +0000
Subject: [PATCH] [AArch64] Mark X16 as clobbered in PAUTH_EPILOGUE for
hint-based PAuthLR
When users request branch protection with PAuthLR on targets that do not
support the PAuthLR instructions, the PAUTH_EPILOGUE falls back to using
hint-space instructions. This fallback sequence uses X16 as a temporary
register, but X16 was not listed in the clobber set.
Because Speculative Load Hardening uses X16, this omission made SLH
incompatible with this PAUTH_EPILOGUE path.
Mark X16 as clobbered so the compiler does not assume X16 is preserved across
the epilogue, restoring compatibility with Speculative Load Hardening and
avoiding incorrect register liveness assumptions. The clobber is added in C++
rather than TableGen, as X16 is only clobbered when PAuthLR is requested as a
branch protection variation and should not be treated as clobbered
unconditionally.
---
.../Target/AArch64/AArch64FrameLowering.cpp | 3 +-
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 16 ++-
llvm/lib/Target/AArch64/AArch64InstrInfo.h | 6 +
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 3 +
.../AArch64/AArch64PrologueEpilogue.cpp | 7 +-
.../sign-return-address-pauthlr-slh.ll | 103 ++++++++++++++++++
6 files changed, 128 insertions(+), 10 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/sign-return-address-pauthlr-slh.ll
diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index 4fb7c62156733..78ccae81f531e 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -1197,8 +1197,7 @@ void AArch64FrameLowering::emitPacRetPlusLeafHardening(
if (MBBI != MBB.end())
DL = MBBI->getDebugLoc();
- BuildMI(MBB, MBBI, DL, TII->get(AArch64::PAUTH_EPILOGUE))
- .setMIFlag(MachineInstr::FrameDestroy);
+ TII->createPauthEpilogueInstr(MBB, DL);
};
// This should be in sync with PEIImpl::calculateSaveRestoreBlocks.
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index f07211325393d..e8b96f6a532bd 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -10719,14 +10719,13 @@ void AArch64InstrInfo::fixupPostOutline(MachineBasicBlock &MBB) const {
static void signOutlinedFunction(MachineFunction &MF, MachineBasicBlock &MBB,
const AArch64InstrInfo *TII,
bool ShouldSignReturnAddr) {
+ assert(&MF.front() == &MBB);
if (!ShouldSignReturnAddr)
return;
BuildMI(MBB, MBB.begin(), DebugLoc(), TII->get(AArch64::PAUTH_PROLOGUE))
.setMIFlag(MachineInstr::FrameSetup);
- BuildMI(MBB, MBB.getFirstInstrTerminator(), DebugLoc(),
- TII->get(AArch64::PAUTH_EPILOGUE))
- .setMIFlag(MachineInstr::FrameDestroy);
+ TII->createPauthEpilogueInstr(MBB, DebugLoc());
}
void AArch64InstrInfo::buildOutlinedFrame(
@@ -11217,6 +11216,17 @@ unsigned llvm::getBLRCallOpcode(const MachineFunction &MF) {
return AArch64::BLR;
}
+void AArch64InstrInfo::createPauthEpilogueInstr(MachineBasicBlock &MBB,
+ DebugLoc DL) const {
+ MachineBasicBlock::iterator InsertPt = MBB.getFirstTerminator();
+ auto Builder = BuildMI(MBB, InsertPt, DL, get(AArch64::PAUTH_EPILOGUE))
+ .setMIFlag(MachineInstr::FrameDestroy);
+
+ const auto *AFI = MBB.getParent()->getInfo<AArch64FunctionInfo>();
+ if (AFI->branchProtectionPAuthLR() && !Subtarget.hasPAuthLR())
+ Builder.addReg(AArch64::X16, RegState::ImplicitDefine);
+}
+
MachineBasicBlock::iterator
AArch64InstrInfo::probedStackAlloc(MachineBasicBlock::iterator MBBI,
Register TargetReg, bool FrameSetup) const {
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h
index 2ccde3e661de5..e40e37b0c526f 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h
@@ -571,6 +571,12 @@ class AArch64InstrInfo final : public AArch64GenInstrInfo {
Register TargetReg,
bool FrameSetup) const;
+ /// Insert a `PAUTH_EPILOGUE` pseudo before the first terminator in \p MBB to
+ /// authenticate the return address. Adds an implicit def of X16 when the
+ /// branch protection uses PAuthLR but the subtarget lacks PAuthLR
+ /// instructions.
+ void createPauthEpilogueInstr(MachineBasicBlock &MBB, DebugLoc DL) const;
+
#define GET_INSTRINFO_HELPER_DECLS
#include "AArch64GenInstrInfo.inc"
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index cb8f7c3d70afc..58a96efee0507 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -2063,6 +2063,9 @@ def PAUTH_PROLOGUE : Pseudo<(outs), (ins), []>, Sched<[]> {
// Insertion point of LR authentication code.
// The RET terminator of the containing machine basic block may be replaced
// with a combined RETA(A|B) instruction when rewriting this Pseudo.
+//
+// In the case where PAuthLR is requested but the subtarget lacks PAuthLR instructions,
+// this pseudo also implicitly defines X16. This is done in C++ code.
def PAUTH_EPILOGUE : Pseudo<(outs), (ins), []>, Sched<[]>;
}
diff --git a/llvm/lib/Target/AArch64/AArch64PrologueEpilogue.cpp b/llvm/lib/Target/AArch64/AArch64PrologueEpilogue.cpp
index a1be940937cdb..432584adbb4a1 100644
--- a/llvm/lib/Target/AArch64/AArch64PrologueEpilogue.cpp
+++ b/llvm/lib/Target/AArch64/AArch64PrologueEpilogue.cpp
@@ -1754,11 +1754,8 @@ void AArch64EpilogueEmitter::finalizeEpilogue() const {
if (AFI->shouldSignReturnAddress(MF)) {
// If pac-ret+leaf is in effect, PAUTH_EPILOGUE pseudo instructions
// are inserted by emitPacRetPlusLeafHardening().
- if (!AFL.shouldSignReturnAddressEverywhere(MF)) {
- BuildMI(MBB, MBB.getFirstTerminator(), DL,
- TII->get(AArch64::PAUTH_EPILOGUE))
- .setMIFlag(MachineInstr::FrameDestroy);
- }
+ if (!AFL.shouldSignReturnAddressEverywhere(MF))
+ TII->createPauthEpilogueInstr(MBB, DL);
// AArch64PointerAuth pass will insert SEH_PACSignLR
HasWinCFI |= NeedsWinCFI;
}
diff --git a/llvm/test/CodeGen/AArch64/sign-return-address-pauthlr-slh.ll b/llvm/test/CodeGen/AArch64/sign-return-address-pauthlr-slh.ll
new file mode 100644
index 0000000000000..f74e369647730
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sign-return-address-pauthlr-slh.ll
@@ -0,0 +1,103 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64-none-linux-gnu %s -o - | FileCheck %s --check-prefix=CHECK-NO-PAUTH
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mattr=+pauth %s -o - | FileCheck %s --check-prefix=CHECK-PAUTH
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mattr=+pauth-lr %s -o - | FileCheck %s --check-prefix=CHECK-PAUTHLR
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mattr=+pauth,+pauth-lr %s -o - | FileCheck %s --check-prefix=CHECK-PAUTH-PAUTHLR
+
+define i32 @f() #0 {
+; CHECK-NO-PAUTH-LABEL: f:
+; CHECK-NO-PAUTH: // %bb.0: // %entry
+; CHECK-NO-PAUTH-NEXT: dsb sy
+; CHECK-NO-PAUTH-NEXT: isb
+; CHECK-NO-PAUTH-NEXT: hint #39
+; CHECK-NO-PAUTH-NEXT: .cfi_negate_ra_state_with_pc
+; CHECK-NO-PAUTH-NEXT: .Ltmp0:
+; CHECK-NO-PAUTH-NEXT: hint #25
+; CHECK-NO-PAUTH-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NO-PAUTH-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NO-PAUTH-NEXT: .cfi_offset w30, -16
+; CHECK-NO-PAUTH-NEXT: hint #7
+; CHECK-NO-PAUTH-NEXT: mov w0, wzr
+; CHECK-NO-PAUTH-NEXT: //APP
+; CHECK-NO-PAUTH-NEXT: //NO_APP
+; CHECK-NO-PAUTH-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NO-PAUTH-NEXT: adrp x16, .Ltmp0
+; CHECK-NO-PAUTH-NEXT: add x16, x16, :lo12:.Ltmp0
+; CHECK-NO-PAUTH-NEXT: hint #39
+; CHECK-NO-PAUTH-NEXT: hint #29
+; CHECK-NO-PAUTH-NEXT: ret
+;
+; CHECK-PAUTH-LABEL: f:
+; CHECK-PAUTH: // %bb.0: // %entry
+; CHECK-PAUTH-NEXT: dsb sy
+; CHECK-PAUTH-NEXT: isb
+; CHECK-PAUTH-NEXT: hint #39
+; CHECK-PAUTH-NEXT: .cfi_negate_ra_state_with_pc
+; CHECK-PAUTH-NEXT: .Ltmp0:
+; CHECK-PAUTH-NEXT: paciasp
+; CHECK-PAUTH-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-PAUTH-NEXT: .cfi_def_cfa_offset 16
+; CHECK-PAUTH-NEXT: .cfi_offset w30, -16
+; CHECK-PAUTH-NEXT: xpaci x30
+; CHECK-PAUTH-NEXT: mov w0, wzr
+; CHECK-PAUTH-NEXT: //APP
+; CHECK-PAUTH-NEXT: //NO_APP
+; CHECK-PAUTH-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-PAUTH-NEXT: adrp x16, .Ltmp0
+; CHECK-PAUTH-NEXT: add x16, x16, :lo12:.Ltmp0
+; CHECK-PAUTH-NEXT: hint #39
+; CHECK-PAUTH-NEXT: retaa
+;
+; CHECK-PAUTHLR-LABEL: f:
+; CHECK-PAUTHLR: // %bb.0: // %entry
+; CHECK-PAUTHLR-NEXT: cmp sp, #0
+; CHECK-PAUTHLR-NEXT: csetm x16, ne
+; CHECK-PAUTHLR-NEXT: .cfi_negate_ra_state_with_pc
+; CHECK-PAUTHLR-NEXT: .Ltmp0:
+; CHECK-PAUTHLR-NEXT: paciasppc
+; CHECK-PAUTHLR-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-PAUTHLR-NEXT: .cfi_def_cfa_offset 16
+; CHECK-PAUTHLR-NEXT: .cfi_offset w30, -16
+; CHECK-PAUTHLR-NEXT: hint #7
+; CHECK-PAUTHLR-NEXT: mov w0, wzr
+; CHECK-PAUTHLR-NEXT: //APP
+; CHECK-PAUTHLR-NEXT: //NO_APP
+; CHECK-PAUTHLR-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-PAUTHLR-NEXT: and x30, x30, x16
+; CHECK-PAUTHLR-NEXT: csdb
+; CHECK-PAUTHLR-NEXT: mov x1, sp
+; CHECK-PAUTHLR-NEXT: autiasppc .Ltmp0
+; CHECK-PAUTHLR-NEXT: and x1, x1, x16
+; CHECK-PAUTHLR-NEXT: mov sp, x1
+; CHECK-PAUTHLR-NEXT: ret
+;
+; CHECK-PAUTH-PAUTHLR-LABEL: f:
+; CHECK-PAUTH-PAUTHLR: // %bb.0: // %entry
+; CHECK-PAUTH-PAUTHLR-NEXT: cmp sp, #0
+; CHECK-PAUTH-PAUTHLR-NEXT: csetm x16, ne
+; CHECK-PAUTH-PAUTHLR-NEXT: .cfi_negate_ra_state_with_pc
+; CHECK-PAUTH-PAUTHLR-NEXT: .Ltmp0:
+; CHECK-PAUTH-PAUTHLR-NEXT: paciasppc
+; CHECK-PAUTH-PAUTHLR-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-PAUTH-PAUTHLR-NEXT: .cfi_def_cfa_offset 16
+; CHECK-PAUTH-PAUTHLR-NEXT: .cfi_offset w30, -16
+; CHECK-PAUTH-PAUTHLR-NEXT: xpaci x30
+; CHECK-PAUTH-PAUTHLR-NEXT: mov w0, wzr
+; CHECK-PAUTH-PAUTHLR-NEXT: //APP
+; CHECK-PAUTH-PAUTHLR-NEXT: //NO_APP
+; CHECK-PAUTH-PAUTHLR-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-PAUTH-PAUTHLR-NEXT: and x30, x30, x16
+; CHECK-PAUTH-PAUTHLR-NEXT: csdb
+; CHECK-PAUTH-PAUTHLR-NEXT: mov x1, sp
+; CHECK-PAUTH-PAUTHLR-NEXT: and x1, x1, x16
+; CHECK-PAUTH-PAUTHLR-NEXT: mov sp, x1
+; CHECK-PAUTH-PAUTHLR-NEXT: retaasppc .Ltmp0
+entry:
+ %0 = tail call ptr @llvm.returnaddress(i32 0)
+ tail call void asm sideeffect "", "r"(ptr %0)
+ ret i32 0
+}
+
+declare ptr @llvm.returnaddress(i32 immarg)
+
+attributes #0 = { speculative_load_hardening "branch-protection-pauth-lr" "sign-return-address"="all" "sign-return-address-key"="a_key" }
More information about the llvm-branch-commits
mailing list