[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