[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:03:23 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-x86
Author: Hugo (hsanzg)
<details>
<summary>Changes</summary>
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@<!-- -->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@<!-- -->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.
---
Full diff: https://github.com/llvm/llvm-project/pull/218276.diff
2 Files Affected:
- (modified) llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp (+5)
- (added) llvm/test/CodeGen/X86/eh-call-site-info-for-inline-asm.ll (+39)
``````````diff
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
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/218276
More information about the llvm-commits
mailing list