Skip to content

Conversation

@shashforge
Copy link
Contributor

@shashforge shashforge commented Aug 25, 2025

[AArch64][BTI] Add BTI at EH entries

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

@llvmbot
Copy link
Member

llvmbot commented Aug 25, 2025

@llvm/pr-subscribers-backend-aarch64

Author: Shashi Shankar (shashforge)

Changes

[AArch64][BTI] Add BTI at EH entries

Mark EH landing pads as indirect-branch targets so we emit bti j on Itanium EH; treat WinEH funclet entries as call-like to emit bti c.

Tests:

  • bti-ehpad.ll: asserts BTI j (or HINT #36) at the landing pad.
  • wineh-bti-funclet.ll: enables +bti and asserts BTI-c (or PAC) at entry;
    use POSIX whitespace classes in FileCheck for portability.

Fixes: #149267


Full diff: https://github.com/llvm/llvm-project/pull/155308.diff

3 Files Affected:

  • (modified) llvm/lib/Target/AArch64/AArch64BranchTargets.cpp (+8-1)
  • (added) llvm/test/CodeGen/AArch64/bti-ehpad.ll (+29)
  • (added) llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll (+34)
diff --git a/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp b/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp
index 3436dc9ef4521..42315ec7eb503 100644
--- a/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp
+++ b/llvm/lib/Target/AArch64/AArch64BranchTargets.cpp
@@ -97,12 +97,19 @@ bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {
          (F.hasAddressTaken() || !F.hasLocalLinkage())))
       CouldCall = true;
 
+
     // If the block itself is address-taken, it could be indirectly branched
     // to, but not called.
     if (MBB.isMachineBlockAddressTaken() || MBB.isIRBlockAddressTaken() ||
-        JumpTableTargets.count(&MBB))
+        JumpTableTargets.count(&MBB) || MBB.isEHPad())
       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;
diff --git a/llvm/test/CodeGen/AArch64/bti-ehpad.ll b/llvm/test/CodeGen/AArch64/bti-ehpad.ll
new file mode 100644
index 0000000000000..1845cca46e72e
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/bti-ehpad.ll
@@ -0,0 +1,29 @@
+; REQUIRES: aarch64-registered-target
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu %s -o - | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
+
+declare i32 @__gxx_personality_v0(...)
+declare void @may_throw()
+
+define void @test() #0 personality ptr @__gxx_personality_v0 {
+entry:
+  invoke void @may_throw()
+          to label %ret unwind label %lpad
+
+lpad:
+  landingpad { ptr, i32 } cleanup
+  ret void
+
+ret:
+  ret void
+}
+
+; Request BTI in codegen.
+attributes #0 = { "branch-target-enforcement"="true" "target-features"="+bti" }
+
+; CHECK-LABEL: test:
+; The assembler prints the label line like: ".LBB0_2: // %lpad"
+; Match the EH pad comment, then the very next line must be BTI j (or HINT #36).
+; CHECK:      {{//[[:space:]]*%lpad}}
+; CHECK-NEXT: {{(bti[[:space:]]+j|hint[[:space:]]+#36)}}
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..31e193b5425d4
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/wineh-bti-funclet.ll
@@ -0,0 +1,34 @@
+; REQUIRES: aarch64-registered-target
+; RUN: llc -mtriple=aarch64-windows -mattr=+bti -o - %s | FileCheck %s
+
+target triple = "aarch64-unknown-windows-msvc"
+
+declare i32 @__CxxFrameHandler3(...)
+declare void @may_throw()
+
+define dso_local void @"?w@@YAXXZ"() #0 personality ptr @__CxxFrameHandler3 {
+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]
+  ; Put some code in the funclet so it materializes with text.
+  call void @may_throw() ["funclet"(token %cp)]
+  catchret from %cp to label %try.cont
+
+try.cont:
+  ret void
+}
+
+; Ask for BTI enforcement at the function level (the RUN line already enables +bti).
+attributes #0 = { "branch-target-enforcement"="true" }
+
+; Function entry should accept call-like entries:
+;  - Either 'hint #34' / 'bti c'
+;  - Or a PAC prologue (paciasp/pacibsp), which also satisfies BTI-c
+; CHECK-LABEL: "?w@@YAXXZ":
+; CHECK: {{(hint[[:space:]]+#34|bti[[:space:]]+c|paciasp|pacibsp)}}

@github-actions
Copy link

github-actions bot commented Aug 25, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@shashforge shashforge changed the title [AArch64][BTI] Add BTI at EH entries: BTI j for Itanium EH, BTI c for… [AArch64][BTI] Add BTI at EH entries. Aug 25, 2025
@ozbenh
Copy link

ozbenh commented Aug 25, 2025

Did you drop the change to ensure the BTI is inserted after EH_LABEL ?

Copy link
Collaborator

@davemgreen davemgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want me to take this over I am happy to do so.

@shashforge
Copy link
Contributor Author

Did you drop the change to ensure the BTI is inserted after EH_LABEL ?

Good catch, Thanks — restored.. We now insert BTI after any leading EH_LABEL and then after meta/CFI, so the first executed instruction in a landing pad is BTI. For non‑EH blocks we keep the legacy order to preserve existing codegen/tests.

Copy link
Collaborator

@davemgreen davemgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work. Any reason to not use update_llc_test_checks.py?

@shashforge
Copy link
Contributor Author

shashforge commented Aug 26, 2025

Nice work. Any reason to not use update_llc_test_checks.py?

Thanks! I’ve switched the tests to utils/update_llc_test_checks.py and pushed the autogenerated CHECK blocks.
I initially kept hand‑written checks to focus on the BTI‑placement invariant, but I agree full‑body checks are better for regression coverage

@shashforge shashforge requested a review from davemgreen August 26, 2025 12:53
Copy link
Collaborator

@davemgreen davemgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. @ozbenh does this do OK in your testing?

@efriedma-quic @DanielKristofKiss does this sound OK to you? I'm not an expert with pacbti and exception handling, but it seems OK to me.

Copy link
Member

@DanielKristofKiss DanielKristofKiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

besides the NITs LGTM.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@davemgreen davemgreen merged commit 1b37b9e into llvm:main Aug 30, 2025
9 checks passed
tru pushed a commit to llvmbot/llvm-project that referenced this pull request Sep 8, 2025
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: llvm#149267

Signed-off-by: Shashi Shankar <[email protected]>
(cherry picked from commit 1b37b9e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clang++ 20 Exceptions crash with (recent) libgcc and aarch64 BTI (Linux)

6 participants