[llvm-branch-commits] [llvm] release/21.x: [AArch64][BTI] Add BTI at EH entries. (#155308) (PR #156170)
David Green via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Aug 31 23:22:20 PDT 2025
https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/156170
>From 51081359fa4a76c9d20489084ce2a399b5bfc520 Mon Sep 17 00:00:00 2001
From: Shashi Shankar <shashishankar1687 at gmail.com>
Date: Sat, 30 Aug 2025 11:56:03 +0200
Subject: [PATCH 1/2] [AArch64][BTI] Add BTI at EH entries. (#155308)
Mark EH landing pads as indirect-branch targets (BTI j) and treat WinEH
funclet entries as call-like (BTI c). Add lit tests for ELF and COFF.
Tests:
Adds lit tests: bti-ehpad.ll and wineh-bti-funclet.ll.
Fixes: #149267
Signed-off-by: Shashi Shankar <shashishankar1687 at gmail.com>
(cherry picked from commit 1b37b9e6d788d7058381b68b5ab265bcb6181335)
---
.../Target/AArch64/AArch64BranchTargets.cpp | 46 ++++++++---
llvm/test/CodeGen/AArch64/bti-ehpad.ll | 44 +++++++++++
.../AArch64/sign-return-address-pauth-lr.ll | 16 ++--
.../test/CodeGen/AArch64/wineh-bti-funclet.ll | 79 +++++++++++++++++++
llvm/test/CodeGen/AArch64/wineh-bti.ll | 2 +-
5 files changed, 167 insertions(+), 20 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/bti-ehpad.ll
create mode 100644 llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
diff --git a/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp b/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp
index 3436dc9ef4521..137ff898e86a3 100644
--- a/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp
+++ b/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp
@@ -30,6 +30,14 @@ using namespace llvm;
#define AARCH64_BRANCH_TARGETS_NAME "AArch64 Branch Targets"
namespace {
+// BTI HINT encoding: base (32) plus 'c' (2) and/or 'j' (4).
+enum : unsigned {
+ BTIBase = 32, // Base immediate for BTI HINT
+ BTIC = 1u << 1, // 2
+ BTIJ = 1u << 2, // 4
+ BTIMask = BTIC | BTIJ,
+};
+
class AArch64BranchTargets : public MachineFunctionPass {
public:
static char ID;
@@ -42,6 +50,7 @@ class AArch64BranchTargets : public MachineFunctionPass {
void addBTI(MachineBasicBlock &MBB, bool CouldCall, bool CouldJump,
bool NeedsWinCFI);
};
+
} // end anonymous namespace
char AArch64BranchTargets::ID = 0;
@@ -62,9 +71,8 @@ bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {
if (!MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())
return false;
- LLVM_DEBUG(
- dbgs() << "********** AArch64 Branch Targets **********\n"
- << "********** Function: " << MF.getName() << '\n');
+ LLVM_DEBUG(dbgs() << "********** AArch64 Branch Targets **********\n"
+ << "********** Function: " << MF.getName() << '\n');
const Function &F = MF.getFunction();
// LLVM does not consider basic blocks which are the targets of jump tables
@@ -103,6 +111,12 @@ bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {
JumpTableTargets.count(&MBB))
CouldJump = true;
+ if (MBB.isEHPad()) {
+ if (HasWinCFI && (MBB.isEHFuncletEntry() || MBB.isCleanupFuncletEntry()))
+ CouldCall = true;
+ else
+ CouldJump = true;
+ }
if (CouldCall || CouldJump) {
addBTI(MBB, CouldCall, CouldJump, HasWinCFI);
MadeChange = true;
@@ -130,7 +144,12 @@ void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall,
auto MBBI = MBB.begin();
- // Skip the meta instructions, those will be removed anyway.
+ // If the block starts with EH_LABEL(s), skip them first.
+ while (MBBI != MBB.end() && MBBI->isEHLabel()) {
+ ++MBBI;
+ }
+
+ // Skip meta/CFI/etc. (and EMITBKEY) to reach the first executable insn.
for (; MBBI != MBB.end() &&
(MBBI->isMetaInstruction() || MBBI->getOpcode() == AArch64::EMITBKEY);
++MBBI)
@@ -138,16 +157,21 @@ void AArch64BranchTargets::addBTI(MachineBasicBlock &MBB, bool CouldCall,
// SCTLR_EL1.BT[01] is set to 0 by default which means
// PACI[AB]SP are implicitly BTI C so no BTI C instruction is needed there.
- if (MBBI != MBB.end() && HintNum == 34 &&
+ if (MBBI != MBB.end() && ((HintNum & BTIMask) == BTIC) &&
(MBBI->getOpcode() == AArch64::PACIASP ||
MBBI->getOpcode() == AArch64::PACIBSP))
return;
- if (HasWinCFI && MBBI->getFlag(MachineInstr::FrameSetup)) {
- BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()),
- TII->get(AArch64::SEH_Nop));
+ // Insert BTI exactly at the first executable instruction.
+ const DebugLoc DL = MBB.findDebugLoc(MBBI);
+ MachineInstr *BTI = BuildMI(MBB, MBBI, DL, TII->get(AArch64::HINT))
+ .addImm(HintNum)
+ .getInstr();
+
+ // WinEH: put .seh_nop after BTI when the first real insn is FrameSetup.
+ if (HasWinCFI && MBBI != MBB.end() &&
+ MBBI->getFlag(MachineInstr::FrameSetup)) {
+ auto AfterBTI = std::next(MachineBasicBlock::iterator(BTI));
+ BuildMI(MBB, AfterBTI, DL, TII->get(AArch64::SEH_Nop));
}
- BuildMI(MBB, MBB.begin(), MBB.findDebugLoc(MBB.begin()),
- TII->get(AArch64::HINT))
- .addImm(HintNum);
}
diff --git a/llvm/test/CodeGen/AArch64/bti-ehpad.ll b/llvm/test/CodeGen/AArch64/bti-ehpad.ll
new file mode 100644
index 0000000000000..674421adaf51c
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/bti-ehpad.ll
@@ -0,0 +1,44 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu %s -o - | FileCheck %s
+
+; Purpose: With BTI enabled, the landing pad (%lpad) begins with an EH_LABEL and the
+; first *executed* instruction is `bti j`. (BTI is inserted *after* the EH label and meta.)
+
+declare i32 @__gxx_personality_v0(...)
+declare void @may_throw()
+
+define void @test() #0 personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: test:
+; CHECK: .Lfunc_begin0:
+; CHECK-NEXT: .cfi_startproc
+; CHECK-NEXT: .cfi_personality 156, DW.ref.__gxx_personality_v0
+; CHECK-NEXT: .cfi_lsda 28, .Lexception0
+; CHECK-NEXT: // %bb.0: // %entry
+; CHECK-NEXT: bti c
+; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT: .cfi_def_cfa_offset 16
+; CHECK-NEXT: .cfi_offset w30, -16
+; CHECK-NEXT: .Ltmp0: // EH_LABEL
+; CHECK-NEXT: bl may_throw
+; CHECK-NEXT: .Ltmp1: // EH_LABEL
+; CHECK-NEXT: // %bb.1: // %common.ret
+; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT: ret
+; CHECK-NEXT: .LBB0_2: // %lpad
+; CHECK-NEXT: .Ltmp2: // EH_LABEL
+; CHECK-NEXT: bti j
+; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT: ret
+entry:
+ invoke void @may_throw()
+ to label %ret unwind label %lpad
+
+lpad:
+ landingpad { ptr, i32 } cleanup
+ ret void
+
+ret:
+ ret void
+}
+
+attributes #0 = { noinline "branch-target-enforcement"="true" "target-features"="+bti" }
diff --git a/llvm/test/CodeGen/AArch64/sign-return-address-pauth-lr.ll b/llvm/test/CodeGen/AArch64/sign-return-address-pauth-lr.ll
index 85aa6846cd800..0091469edde92 100644
--- a/llvm/test/CodeGen/AArch64/sign-return-address-pauth-lr.ll
+++ b/llvm/test/CodeGen/AArch64/sign-return-address-pauth-lr.ll
@@ -507,8 +507,8 @@ define i32 @leaf_sign_all_a_key_bti(i32 %x) "branch-protection-pauth-lr" "sign-r
;
; PAUTHLR-LABEL: leaf_sign_all_a_key_bti:
; PAUTHLR: // %bb.0:
-; PAUTHLR-NEXT: bti c
; PAUTHLR-NEXT: .cfi_negate_ra_state_with_pc
+; PAUTHLR-NEXT: bti c
; PAUTHLR-NEXT: .Ltmp10:
; PAUTHLR-NEXT: paciasppc
; PAUTHLR-NEXT: adrp x16, .Ltmp10
@@ -521,8 +521,8 @@ define i32 @leaf_sign_all_a_key_bti(i32 %x) "branch-protection-pauth-lr" "sign-r
define i32 @leaf_sign_all_b_key_bti(i32 %x) "branch-protection-pauth-lr" "sign-return-address"="all" "sign-return-address-key"="b_key" "branch-target-enforcement" {
; COMPAT-LABEL: leaf_sign_all_b_key_bti:
; COMPAT: // %bb.0:
+; COMPAT-NEXT: .cfi_b_key_frame
; COMPAT-NEXT: hint #34
-; COMPAT-NEXT: .cfi_b_key_frame
; COMPAT-NEXT: hint #39
; COMPAT-NEXT: .cfi_negate_ra_state_with_pc
; COMPAT-NEXT: .Ltmp11:
@@ -535,8 +535,8 @@ define i32 @leaf_sign_all_b_key_bti(i32 %x) "branch-protection-pauth-lr" "sign-r
;
; V83A-LABEL: leaf_sign_all_b_key_bti:
; V83A: // %bb.0:
-; V83A-NEXT: hint #34
; V83A-NEXT: .cfi_b_key_frame
+; V83A-NEXT: hint #34
; V83A-NEXT: hint #39
; V83A-NEXT: .cfi_negate_ra_state_with_pc
; V83A-NEXT: .Ltmp11:
@@ -548,9 +548,9 @@ define i32 @leaf_sign_all_b_key_bti(i32 %x) "branch-protection-pauth-lr" "sign-r
;
; PAUTHLR-LABEL: leaf_sign_all_b_key_bti:
; PAUTHLR: // %bb.0:
-; PAUTHLR-NEXT: bti c
; PAUTHLR-NEXT: .cfi_b_key_frame
; PAUTHLR-NEXT: .cfi_negate_ra_state_with_pc
+; PAUTHLR-NEXT: bti c
; PAUTHLR-NEXT: .Ltmp11:
; PAUTHLR-NEXT: pacibsppc
; PAUTHLR-NEXT: adrp x16, .Ltmp11
@@ -563,9 +563,9 @@ define i32 @leaf_sign_all_b_key_bti(i32 %x) "branch-protection-pauth-lr" "sign-r
define i32 @leaf_sign_all_v83_b_key_bti(i32 %x) "branch-protection-pauth-lr" "sign-return-address"="all" "target-features"="+v8.3a" "sign-return-address-key"="b_key" "branch-target-enforcement" {
; CHECK-LABEL: leaf_sign_all_v83_b_key_bti:
; CHECK: // %bb.0:
-; CHECK-NEXT: hint #34
-; CHECK-NEXT: .cfi_b_key_frame
-; CHECK-NEXT: hint #39
+; CHECK-NEXT: .cfi_b_key_frame
+; CHECK-NEXT: hint #34
+; CHECK-NEXT: hint #39
; CHECK-NEXT: .cfi_negate_ra_state_with_pc
; CHECK-NEXT: .Ltmp12:
; CHECK-NEXT: pacibsp
@@ -576,9 +576,9 @@ define i32 @leaf_sign_all_v83_b_key_bti(i32 %x) "branch-protection-pauth-lr" "si
;
; PAUTHLR-LABEL: leaf_sign_all_v83_b_key_bti:
; PAUTHLR: // %bb.0:
-; PAUTHLR-NEXT: bti c
; PAUTHLR-NEXT: .cfi_b_key_frame
; PAUTHLR-NEXT: .cfi_negate_ra_state_with_pc
+; PAUTHLR-NEXT: bti c
; PAUTHLR-NEXT: .Ltmp12:
; PAUTHLR-NEXT: pacibsppc
; PAUTHLR-NEXT: adrp x16, .Ltmp12
diff --git a/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll b/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
new file mode 100644
index 0000000000000..4f4f984b8974b
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
@@ -0,0 +1,79 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=aarch64-windows -mattr=+bti -o - %s | FileCheck %s
+
+declare i32 @__CxxFrameHandler3(...)
+declare void @may_throw()
+
+; Purpose: For WinEH funclets, entry is call-like: accept `bti c` / `hint #34` or a PAC prologue.
+
+define dso_local void @wineh_funclet() #0 personality ptr @__CxxFrameHandler3 {
+; CHECK-LABEL: wineh_funclet:
+; CHECK: .Lfunc_begin0:
+; CHECK-NEXT: .seh_proc wineh_funclet
+; CHECK-NEXT: .seh_handler __CxxFrameHandler3, @unwind, @except
+; CHECK-NEXT: // %bb.0: // %entry
+; CHECK-NEXT: bti c
+; CHECK-NEXT: .seh_nop
+; CHECK-NEXT: stp x29, x30, [sp, #-32]! // 16-byte Folded Spill
+; CHECK-NEXT: .seh_save_fplr_x 32
+; CHECK-NEXT: mov x29, sp
+; CHECK-NEXT: .seh_set_fp
+; CHECK-NEXT: .seh_endprologue
+; CHECK-NEXT: mov x0, #-2 // =0xfffffffffffffffe
+; CHECK-NEXT: stur x0, [x29, #16]
+; CHECK-NEXT: .Ltmp0: // EH_LABEL
+; CHECK-NEXT: bl may_throw
+; CHECK-NEXT: .Ltmp1: // EH_LABEL
+; CHECK-NEXT: .LBB0_1: // Block address taken
+; CHECK-NEXT: // %try.cont
+; CHECK-NEXT: $ehgcr_0_1:
+; CHECK-NEXT: bti j
+; CHECK-NEXT: .seh_startepilogue
+; CHECK-NEXT: ldp x29, x30, [sp], #32 // 16-byte Folded Reload
+; CHECK-NEXT: .seh_save_fplr_x 32
+; CHECK-NEXT: .seh_endepilogue
+; CHECK-NEXT: ret
+; CHECK-NEXT: .seh_endfunclet
+; CHECK-NEXT: .seh_handlerdata
+; CHECK-NEXT: .word $cppxdata$wineh_funclet at IMGREL
+; CHECK-NEXT: .text
+; CHECK-NEXT: .seh_endproc
+; CHECK-NEXT: .def "?catch$2@?0?wineh_funclet at 4HA";
+; CHECK-NEXT: .scl 3;
+; CHECK-NEXT: .type 32;
+; CHECK-NEXT: .endef
+; CHECK-NEXT: .p2align 2
+; CHECK-NEXT: "?catch$2@?0?wineh_funclet at 4HA":
+; CHECK-NEXT: .seh_proc "?catch$2@?0?wineh_funclet at 4HA"
+; CHECK-NEXT: .seh_handler __CxxFrameHandler3, @unwind, @except
+; CHECK-NEXT: .LBB0_2: // %catch
+; CHECK-NEXT: bti c
+; CHECK-NEXT: .seh_nop
+; CHECK-NEXT: stp x29, x30, [sp, #-16]! // 16-byte Folded Spill
+; CHECK-NEXT: .seh_save_fplr_x 16
+; CHECK-NEXT: .seh_endprologue
+; CHECK-NEXT: bl may_throw
+; CHECK-NEXT: adrp x0, .LBB0_1
+; CHECK-NEXT: add x0, x0, .LBB0_1
+; CHECK-NEXT: .seh_startepilogue
+; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload
+; CHECK-NEXT: .seh_save_fplr_x 16
+; CHECK-NEXT: .seh_endepilogue
+; CHECK-NEXT: ret
+entry:
+ invoke void @may_throw()
+ to label %try.cont unwind label %catch.dispatch
+
+catch.dispatch:
+ %cs = catchswitch within none [label %catch] unwind to caller
+
+catch:
+ %cp = catchpad within %cs [ptr null, i32 0, ptr null]
+ call void @may_throw() ["funclet"(token %cp)]
+ catchret from %cp to label %try.cont
+
+try.cont:
+ ret void
+}
+
+attributes #0 = { noinline "branch-target-enforcement"="true" }
diff --git a/llvm/test/CodeGen/AArch64/wineh-bti.ll b/llvm/test/CodeGen/AArch64/wineh-bti.ll
index 8b8960d37f9ef..86555a7f64366 100644
--- a/llvm/test/CodeGen/AArch64/wineh-bti.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-bti.ll
@@ -29,7 +29,7 @@ lbl4:
; CHECK-LABEL: func:
; CHECK-NEXT: .seh_proc func
-; CHECK-NEXT: // %bb.0:
+; CHECK: // %bb.0: // %entry
; CHECK-NEXT: hint #34
; CHECK-NEXT: .seh_nop
; CHECK-NEXT: str x19, [sp, #-16]!
>From 910b1ef507173197ab687271a0b9f3e99fa159d0 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Mon, 1 Sep 2025 07:20:47 +0100
Subject: [PATCH 2/2] Remove EH_LABEL comments from tests
---
llvm/test/CodeGen/AArch64/bti-ehpad.ll | 6 +++---
llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/bti-ehpad.ll b/llvm/test/CodeGen/AArch64/bti-ehpad.ll
index 674421adaf51c..dee4bd3ec8adf 100644
--- a/llvm/test/CodeGen/AArch64/bti-ehpad.ll
+++ b/llvm/test/CodeGen/AArch64/bti-ehpad.ll
@@ -18,14 +18,14 @@ define void @test() #0 personality ptr @__gxx_personality_v0 {
; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: .cfi_offset w30, -16
-; CHECK-NEXT: .Ltmp0: // EH_LABEL
+; CHECK-NEXT: .Ltmp0:
; CHECK-NEXT: bl may_throw
-; CHECK-NEXT: .Ltmp1: // EH_LABEL
+; CHECK-NEXT: .Ltmp1:
; CHECK-NEXT: // %bb.1: // %common.ret
; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
; CHECK-NEXT: .LBB0_2: // %lpad
-; CHECK-NEXT: .Ltmp2: // EH_LABEL
+; CHECK-NEXT: .Ltmp2:
; CHECK-NEXT: bti j
; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
; CHECK-NEXT: ret
diff --git a/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll b/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
index 4f4f984b8974b..3a1eaf04af5ab 100644
--- a/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
@@ -21,9 +21,9 @@ define dso_local void @wineh_funclet() #0 personality ptr @__CxxFrameHandler3 {
; CHECK-NEXT: .seh_endprologue
; CHECK-NEXT: mov x0, #-2 // =0xfffffffffffffffe
; CHECK-NEXT: stur x0, [x29, #16]
-; CHECK-NEXT: .Ltmp0: // EH_LABEL
+; CHECK-NEXT: .Ltmp0:
; CHECK-NEXT: bl may_throw
-; CHECK-NEXT: .Ltmp1: // EH_LABEL
+; CHECK-NEXT: .Ltmp1:
; CHECK-NEXT: .LBB0_1: // Block address taken
; CHECK-NEXT: // %try.cont
; CHECK-NEXT: $ehgcr_0_1:
More information about the llvm-branch-commits
mailing list