[llvm] [Thumb2] Use BXAUT instruction if available (PR #183056)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 3 07:21:33 PST 2026
https://github.com/walkerkd updated https://github.com/llvm/llvm-project/pull/183056
>From 9d331ad3f6d9538a24a62ace6ec14f7c1a915d3d Mon Sep 17 00:00:00 2001
From: Keith Walker <keith.walker at arm.com>
Date: Tue, 24 Feb 2026 11:44:43 +0000
Subject: [PATCH 1/2] [Thumb2] Use BXAUT instruction if available
Generated a
bxaut r12, lr, sp
instruction rather than
aut r12, lr, sp
bx lr
The bxaut instruction is available when for thumb2 code with the
armv8.1m-main architecture and PACBTI is enabled
This change introduces a new pseudo instruction ARM::t2BXAUT_RET
which is similar to the existing pseudo instruction ARM::tBX_RET.
---
llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp | 2 +-
llvm/lib/Target/ARM/ARMFrameLowering.cpp | 14 +-
llvm/lib/Target/ARM/ARMInstrThumb2.td | 4 +
llvm/test/CodeGen/Thumb2/pacbti-m-bxaut.ll | 158 +++++++++++++++++++++
4 files changed, 175 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/CodeGen/Thumb2/pacbti-m-bxaut.ll
diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
index bc5a89bc5d7f4..50b1c4c159d13 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
@@ -5692,7 +5692,7 @@ static bool isLRAvailable(const TargetRegisterInfo &TRI,
unsigned Opcode = MI.getOpcode();
if (Opcode == ARM::BX_RET || Opcode == ARM::MOVPCLR ||
Opcode == ARM::SUBS_PC_LR || Opcode == ARM::tBX_RET ||
- Opcode == ARM::tBXNS_RET) {
+ Opcode == ARM::tBXNS_RET || Opcode == ARM::t2BXAUT_RET) {
// These instructions use LR, but it's not an (explicit or implicit)
// operand.
Live = true;
diff --git a/llvm/lib/Target/ARM/ARMFrameLowering.cpp b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
index a0b975072bd8e..7d6be065d208a 100644
--- a/llvm/lib/Target/ARM/ARMFrameLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMFrameLowering.cpp
@@ -623,6 +623,7 @@ static MachineBasicBlock::iterator insertSEH(MachineBasicBlock::iterator MBBI,
break;
case ARM::tBX_RET:
+ case ARM::t2BXAUT_RET:
case ARM::TCRETURNri:
case ARM::TCRETURNrinotr12:
MIB = BuildMI(MF, DL, TII.get(ARM::SEH_Nop_Ret))
@@ -1551,8 +1552,17 @@ void ARMFrameLowering::emitEpilogue(MachineFunction &MF,
// function, the validation instruction is emitted during expansion of the
// tBXNS_RET, since the validation must use the value of SP at function
// entry, before saving, resp. after restoring, FPCXTNS.
- if (AFI->shouldSignReturnAddress() && !AFI->isCmseNSEntryFunction())
- BuildMI(MBB, MBBI, DebugLoc(), STI.getInstrInfo()->get(ARM::t2AUT));
+ if (AFI->shouldSignReturnAddress() && !AFI->isCmseNSEntryFunction()) {
+ bool CanUseBXAut =
+ STI.isThumb() && STI.hasV8_1MMainlineOps() && STI.hasPACBTI();
+ auto TMBBI = MBB.getFirstTerminator();
+ bool IsBXReturn =
+ TMBBI != MBB.end() && TMBBI->getOpcode() == ARM::tBX_RET;
+ if (IsBXReturn && CanUseBXAut)
+ TMBBI->setDesc(STI.getInstrInfo()->get(ARM::t2BXAUT_RET));
+ else
+ BuildMI(MBB, MBBI, DebugLoc(), STI.getInstrInfo()->get(ARM::t2AUT));
+ }
}
if (MF.hasWinCFI()) {
diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td
index 596196c4d0425..998eeae22c17c 100644
--- a/llvm/lib/Target/ARM/ARMInstrThumb2.td
+++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td
@@ -5879,6 +5879,10 @@ let isBranch = 1, isTerminator = 1, isIndirectBranch = 1 in {
}
}
+let isReturn = 1, isTerminator = 1, isBarrier = 1 in {
+ def t2BXAUT_RET : tPseudoExpand<(outs), (ins pred:$p), 2, IIC_Br,
+ [(ARMretglue)], (t2BXAUT pred:$p, R12, LR, SP)>, Sched<[WriteBr]>;
+}
class PACBTIHintSpaceInst<string asm, string ops, bits<8> imm>
: Thumb2XI<(outs), (ins), AddrModeNone, 4, NoItinerary, !strconcat(asm, "\t", ops), "", []>,
diff --git a/llvm/test/CodeGen/Thumb2/pacbti-m-bxaut.ll b/llvm/test/CodeGen/Thumb2/pacbti-m-bxaut.ll
new file mode 100644
index 0000000000000..2a46f07af61e2
--- /dev/null
+++ b/llvm/test/CodeGen/Thumb2/pacbti-m-bxaut.ll
@@ -0,0 +1,158 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc --force-dwarf-frame-section %s -o - | FileCheck %s
+; RUN: llc --filetype=obj %s -o - | llvm-readelf -s --unwind - | FileCheck %s --check-prefix=UNWIND
+target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "thumbv8m.main-none-none-eabi"
+
+; int g(int);
+;
+; #if __ARM_FEATURE_CMSE == 3
+; #define ENTRY __attribute__((cmse_nonsecure_entry))
+; #else
+; #define ENTRY
+; #endif
+;
+; ENTRY int f(int x) {
+; return 1 + g(x - 1);
+; }
+
+define hidden i32 @f0(i32 %x) local_unnamed_addr #0 {
+; CHECK-LABEL: f0:
+; CHECK: .cfi_sections .debug_frame
+; CHECK-NEXT: .cfi_startproc
+; CHECK-NEXT: @ %bb.0: @ %entry
+; CHECK-NEXT: pac r12, lr, sp
+; CHECK-NEXT: .save {r7, ra_auth_code, lr}
+; CHECK-NEXT: push.w {r7, r12, lr}
+; CHECK-NEXT: .cfi_def_cfa_offset 12
+; CHECK-NEXT: .cfi_offset lr, -4
+; CHECK-NEXT: .cfi_offset ra_auth_code, -8
+; CHECK-NEXT: .cfi_offset r7, -12
+; CHECK-NEXT: .pad #4
+; CHECK-NEXT: sub sp, #4
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: subs r0, #1
+; CHECK-NEXT: bl g
+; CHECK-NEXT: adds r0, #1
+; CHECK-NEXT: add sp, #4
+; CHECK-NEXT: pop.w {r7, r12, lr}
+; CHECK-NEXT: bxaut r12, lr, sp
+entry:
+ %sub = add nsw i32 %x, -1
+ %call = tail call i32 @g(i32 %sub)
+ %add = add nsw i32 %call, 1
+ ret i32 %add
+}
+
+define hidden i32 @f1(i32 %x) local_unnamed_addr #1 {
+; CHECK-LABEL: f1:
+; CHECK: .cfi_startproc
+; CHECK-NEXT: @ %bb.0: @ %entry
+; CHECK-NEXT: pac r12, lr, sp
+; CHECK-NEXT: vstr fpcxtns, [sp, #-4]!
+; CHECK-NEXT: .cfi_def_cfa_offset 4
+; CHECK-NEXT: .save {r7, ra_auth_code, lr}
+; CHECK-NEXT: push.w {r7, r12, lr}
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: .cfi_offset lr, -8
+; CHECK-NEXT: .cfi_offset ra_auth_code, -12
+; CHECK-NEXT: .cfi_offset r7, -16
+; CHECK-NEXT: subs r0, #1
+; CHECK-NEXT: bl g
+; CHECK-NEXT: adds r0, #1
+; CHECK-NEXT: pop.w {r7, r12, lr}
+; CHECK-NEXT: vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, vpr}
+; CHECK-NEXT: vldr fpcxtns, [sp], #4
+; CHECK-NEXT: aut r12, lr, sp
+; CHECK-NEXT: clrm {r1, r2, r3, r12, apsr}
+; CHECK-NEXT: bxns lr
+entry:
+ %sub = add nsw i32 %x, -1
+ %call = tail call i32 @g(i32 %sub)
+ %add = add nsw i32 %call, 1
+ ret i32 %add
+}
+
+define hidden i32 @f2(i32 %x) local_unnamed_addr #2 {
+; CHECK-LABEL: f2:
+; CHECK: .cfi_startproc
+; CHECK-NEXT: @ %bb.0: @ %entry
+; CHECK-NEXT: pac r12, lr, sp
+; CHECK-NEXT: .save {r7, ra_auth_code, lr}
+; CHECK-NEXT: push.w {r7, r12, lr}
+; CHECK-NEXT: .cfi_def_cfa_offset 12
+; CHECK-NEXT: .cfi_offset lr, -4
+; CHECK-NEXT: .cfi_offset ra_auth_code, -8
+; CHECK-NEXT: .cfi_offset r7, -12
+; CHECK-NEXT: .pad #4
+; CHECK-NEXT: sub sp, #4
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: subs r0, #1
+; CHECK-NEXT: bl g
+; CHECK-NEXT: adds r0, #1
+; CHECK-NEXT: add sp, #4
+; CHECK-NEXT: pop.w {r7, r12, lr}
+; CHECK-NEXT: aut r12, lr, sp
+; CHECK-NEXT: mrs r12, control
+; CHECK-NEXT: tst.w r12, #8
+; CHECK-NEXT: beq .LBB2_2
+; CHECK-NEXT: @ %bb.1: @ %entry
+; CHECK-NEXT: vmrs r12, fpscr
+; CHECK-NEXT: vmov d0, lr, lr
+; CHECK-NEXT: vmov d1, lr, lr
+; CHECK-NEXT: vmov d2, lr, lr
+; CHECK-NEXT: vmov d3, lr, lr
+; CHECK-NEXT: vmov d4, lr, lr
+; CHECK-NEXT: vmov d5, lr, lr
+; CHECK-NEXT: vmov d6, lr, lr
+; CHECK-NEXT: vmov d7, lr, lr
+; CHECK-NEXT: bic r12, r12, #159
+; CHECK-NEXT: bic r12, r12, #4026531840
+; CHECK-NEXT: vmsr fpscr, r12
+; CHECK-NEXT: .LBB2_2: @ %entry
+; CHECK-NEXT: mov r1, lr
+; CHECK-NEXT: mov r2, lr
+; CHECK-NEXT: mov r3, lr
+; CHECK-NEXT: mov r12, lr
+; CHECK-NEXT: msr apsr_nzcvq, lr
+; CHECK-NEXT: bxns lr
+entry:
+ %sub = add nsw i32 %x, -1
+ %call = tail call i32 @g(i32 %sub)
+ %add = add nsw i32 %call, 1
+ ret i32 %add
+}
+
+declare dso_local i32 @g(i32) local_unnamed_addr
+
+attributes #0 = { "sign-return-address"="non-leaf" "target-features"="+8msecext,+armv8.1-m.main,+pacbti"}
+attributes #1 = { "sign-return-address"="non-leaf" "cmse_nonsecure_entry" "target-features"="+8msecext,+armv8.1-m.main,+pacbti"}
+attributes #2 = { "sign-return-address"="non-leaf" "cmse_nonsecure_entry" "target-features"="+8msecext,+armv8-m.main,+pacbti,+fp-armv8d16"}
+
+!llvm.module.flags = !{!0, !1, !2}
+
+!0 = !{i32 8, !"branch-target-enforcement", i32 0}
+!1 = !{i32 8, !"sign-return-address", i32 1}
+!2 = !{i32 8, !"sign-return-address-all", i32 0}
+
+; UNWIND-LABEL: FunctionAddress: 0x0
+; UNWIND: 0x00 ; vsp = vsp + 4
+; UNWIND-NEXT: 0x80 0x08 ; pop {r7}
+; UNWIND-NEXT: 0xB4 ; pop ra_auth_code
+; UNWIND-NEXT: 0x84 0x00 ; pop {lr}
+
+
+; UNWIND-LABEL: FunctionAddress: 0x1C
+; UNWIND: 0x80 0x08 ; pop {r7}
+; UNWIND-NEXT: 0xB4 ; pop ra_auth_code
+; UNWIND-NEXT: 0x84 0x00 ; pop {lr}
+
+; UNWIND-LABEL: FunctionAddress: 0x46
+; UNWIND: 0x00 ; vsp = vsp + 4
+; UNWIND-NEXT: 0x80 0x08 ; pop {r7}
+; UNWIND-NEXT: 0xB4 ; pop ra_auth_code
+; UNWIND-NEXT: 0x84 0x00 ; pop {lr}
+
+; UNWIND-LABEL: 00000001 {{.*}} f0
+; UNWIND-LABEL: 0000001d {{.*}} f1
+; UNWIND-LABEL: 00000047 {{.*}} f2
>From b73b54dacff0ffc2f6937a4d8edc6d0093f1b199 Mon Sep 17 00:00:00 2001
From: walkerkd <mr.walker.k.d+github at gmail.com>
Date: Tue, 24 Feb 2026 14:41:02 +0000
Subject: [PATCH 2/2] Update llvm/lib/Target/ARM/ARMInstrThumb2.td
Co-authored-by: Copilot <175728472+Copilot at users.noreply.github.com>
---
llvm/lib/Target/ARM/ARMInstrThumb2.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td
index 998eeae22c17c..2d4d8d274edea 100644
--- a/llvm/lib/Target/ARM/ARMInstrThumb2.td
+++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td
@@ -5880,7 +5880,7 @@ let isBranch = 1, isTerminator = 1, isIndirectBranch = 1 in {
}
let isReturn = 1, isTerminator = 1, isBarrier = 1 in {
- def t2BXAUT_RET : tPseudoExpand<(outs), (ins pred:$p), 2, IIC_Br,
+ def t2BXAUT_RET : tPseudoExpand<(outs), (ins pred:$p), 4, IIC_Br,
[(ARMretglue)], (t2BXAUT pred:$p, R12, LR, SP)>, Sched<[WriteBr]>;
}
More information about the llvm-commits
mailing list