[llvm] Emit LSDA call site info for inline asm calls marked `unwind` (PR #218276)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 12:02:32 PDT 2026
https://github.com/hsanzg created https://github.com/llvm/llvm-project/pull/218276
LLVM currently doesn't consider inline asm calls with the `unwind`
keyword when generating the list of LSDA call sites of a function.
For example, given the following IR
```llvm
declare i32 @rust_eh_personality(i32, i32, i64, ptr, ptr)
declare void @bar()
define void @example() personality ptr @rust_eh_personality {
entry:
call void asm sideeffect alignstack inteldialect unwind "call foo", ""()
invoke void @bar()
to label %cont unwind label %lpad
cont:
ret void
lpad:
%eh = landingpad { ptr, i32 }
cleanup
resume { ptr, i32 } %eh
}
```
`llc` (trunk) produces the following assembly with `-mtriple=x86_64-pc-linux` ([Godbolt](https://godbolt.org/z/x7ojY7K8M) with full output):
```asm
# ...
example: # @example
.Lfunc_begin0:
.cfi_startproc
.cfi_personality 3, rust_eh_personality
.cfi_lsda 3, .Lexception0
# %bb.0: # %entry
push rax
.cfi_def_cfa_offset 16
#APP
call foo
#NO_APP
.Ltmp0: # EH_LABEL
call bar at PLT
.Ltmp1: # EH_LABEL
# %bb.1: # %cont
pop rax
.cfi_def_cfa_offset 8
ret
.LBB0_2: # %lpad
.cfi_def_cfa_offset 16
.Ltmp2: # EH_LABEL
mov rdi, rax
call _Unwind_Resume at PLT
.Lfunc_end0:
# ...
GCC_except_table0:
.Lexception0:
.byte 255 # @LPStart Encoding = omit
.byte 255 # @TType Encoding = omit
.byte 1 # Call site Encoding = uleb128
.uleb128 .Lcst_end0-.Lcst_begin0
.Lcst_begin0:
.uleb128 .Ltmp0-.Lfunc_begin0 # >> Call Site 1 <<
.uleb128 .Ltmp1-.Ltmp0 # Call between .Ltmp0 and .Ltmp1
.uleb128 .Ltmp2-.Lfunc_begin0 # jumps to .Ltmp2
.byte 0 # On action: cleanup
.uleb128 .Ltmp1-.Lfunc_begin0 # >> Call Site 2 <<
.uleb128 .Lfunc_end0-.Ltmp1 # Call between .Ltmp1 and .Lfunc_end0
.byte 0 # has no landing pad
.byte 0 # On action: cleanup
.Lcst_end0:
```
Since no call site entry of `example` covers the `call foo` instruction, if
`foo` ends up unwinding, then the unwinder returns `_URC_FATAL_PHASE1_ERROR`
or segfaults while trying to determine the action to take when visiting the
frame for `example`.
This merge request generalizes `EHStreamer::computeCallSiteTable` to treat
inline asm calls with the `unwind` keyword as regular calls that may unwind.
This then leads LLVM to produce the missing call site entry, from `.Lfunc_begin0`
to `.Ltmp0`.
There's another suspicious use of `callToNoUnwindFunction` in `InvokeStateChangeIterator::scan`
that may need the same treatment, but I don't know enough about its purpose
to be sure. I can change it as well if you deem it necessary.
Tagging @Amanieu for reviewal, since he originally suggested this fix (well, the basic idea) in https://github.com/rust-lang/rust/issues/161490.
AI tool usage disclaimer: Used Claude to minimize the original IR output by Rust into the reproducer shown above.
>From 6c0ad5b71a68bee3850f7e588f746292af19339a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hugo=20Sanz=20Gonz=C3=A1lez?= <hugo at hgsg.me>
Date: Sun, 23 Aug 2026 02:22:30 +0200
Subject: [PATCH] Emit call site info for inline asm calls with `unwind`
Invokes for inline asm already emit the corresponding call site entry in
the LSDA. Do the same for regular inline asm `call`s that may unwind,
with a zero/continue landing pad address.
---
llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp | 5 +++
.../X86/eh-call-site-info-for-inline-asm.ll | 39 +++++++++++++++++++
2 files changed, 44 insertions(+)
create mode 100644 llvm/test/CodeGen/X86/eh-call-site-info-for-inline-asm.ll
diff --git a/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp b/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp
index db6242e6523c0..9b2bb512d23ef 100644
--- a/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp
@@ -267,6 +267,11 @@ void EHStreamer::computeCallSiteTable(
if (!MI.isEHLabel()) {
if (MI.isCall())
SawPotentiallyThrowing |= !callToNoUnwindFunction(&MI);
+ else if (MI.isInlineAsm()) {
+ // An inline asm call may unwind iff it contains the `unwind` keyword.
+ unsigned ExtraInfo = MI.getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
+ SawPotentiallyThrowing |= ExtraInfo & InlineAsm::Extra_MayUnwind;
+ }
continue;
}
diff --git a/llvm/test/CodeGen/X86/eh-call-site-info-for-inline-asm.ll b/llvm/test/CodeGen/X86/eh-call-site-info-for-inline-asm.ll
new file mode 100644
index 0000000000000..40aaa73fa8f0b
--- /dev/null
+++ b/llvm/test/CodeGen/X86/eh-call-site-info-for-inline-asm.ll
@@ -0,0 +1,39 @@
+; RUN: llc < %s -mtriple=x86_64-pc-linux | FileCheck %s
+; Test that we emit call site info for inline asm calls that may unwind.
+
+declare i32 @__my_personality_v0(...)
+declare void @might_throw()
+
+define void @foo() personality ptr @__my_personality_v0 {
+; CHECK: .cfi_personality 3, __my_personality_v0
+; CHECK: .Lcst_begin0:
+; CHECK-NEXT: .uleb128 .Lfunc_begin0-.Lfunc_begin0
+; CHECK-NEXT: .uleb128 .Ltmp0-.Lfunc_begin0
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .uleb128 .Ltmp0-.Lfunc_begin0
+; CHECK-NEXT: .uleb128 .Ltmp1-.Ltmp0
+; CHECK-NEXT: .uleb128 .Ltmp2-.Lfunc_begin0
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .uleb128 .Ltmp1-.Lfunc_begin0
+; CHECK-NEXT: .uleb128 .Lfunc_end0-.Ltmp1
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .Lcst_end0:
+
+ ; An inline asm call that may unwind but has no landing pad.
+ call void asm sideeffect alignstack inteldialect unwind "call ${0:P}", "X"(ptr @might_throw)
+
+ ; An inline asm invoke with a landing pad.
+ invoke void asm sideeffect alignstack inteldialect unwind
+ "call ${0:P}", "X"(ptr @might_throw)
+ to label %cont unwind label %lpad
+
+cont:
+ ret void
+
+lpad:
+ %eh = landingpad { ptr, i32 }
+ cleanup
+ resume { ptr, i32 } %eh
+}
More information about the llvm-commits
mailing list