rust-lang / rust-lang/rust

Inlined `asm!` block with `may_unwind` is missing LSDA call site info

Open
#161,490 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug needs-triage
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

(I apologize if some info is incorrect, since this is the first time I'm dealing with LSDA, LLVM's IR, and unwinding.)

(There's a smaller reproducer in the next comment; you can ignore the coroutine-specific stuff in this description.)

Consider the following function:

#![feature(asm_unwind)]

#[repr(C)]
struct Control {
	stack_ptr: NonNull<()>,
	// ...
}

#[inline(never)]
pub unsafe fn suspend_coro(active: &mut Control) {
  unsafe {
    asm!(
    	// ...
      "ret",
      "2:",
      // ...
      in("rdi") active,
      in("rsi") active.stack_ptr.as_ptr(),
      clobber_abi("sysv64"),
      lateout("r12") _, lateout("r13") _, lateout("r14") _, lateout("r15") _,
      sp_off = const offset_of!(Control, stack_ptr),
      options(may_unwind),
    )
  };
}

When a coroutine is "suspended," the Control structure contains (among other things) a pointer to local label 2.
I've been implementing coroutine stack unwinding via the unstable asm_unwind feature,
and everything works smoothly. (Inspired by corosensei, a force_unwind
function switches to the coroutine stack, and manually creates a frame on top of the one for suspend_coro for
a function that immediately calls _Unwind_RaiseException/std::panic::resume_unwind.)

However, unwinding breaks when I replace #[inline(never)] by #[inline(always)] in suspend_coro.
The reason, I think, is that suspend_coro gets inlined into a function with a cleanup function (and
the rust_eh_personality personality), and Rust/LLVM does not produce the necessary LSDA info for the
unwinder to locate the cleanup function when the frame points to the local label 2 (or more generally,
to any instruction within the asm! block).

To make sure that this is what's happening, I modified Rust (specifically function find_eh_action in
src/sys/personality/dwarf/eh.rs; here's the patch) and libunwind to output all relevant info during unwinding.
The frame pointing to the function with the partly missing LSDA info (and the inlined assembly) leads
the personality function to return _URC_FATAL_PHASE1_ERROR, because it cannot find any landing pad
for the instruction at 2: within the inlined asm! block:

reading dwarf code at lsda=0x591a69ad3e38, func_start=0x591a69afae10
  start encoding: ff
  ttype encoding: ff
  call site encoding: 1
  action table start: 0x591a69ad3e57, ip=0x591a69afaed3
  cs_start=37
  cs_len=5
  cs_lpad=309
  cs_action_entry=0
  cs_start=239
  cs_len=6
  cs_lpad=325
  cs_action_entry=0
  passed ip, not present

thread 'resume_from_different_coros' (355403) panicked at src/unwind/itanium.rs:83:5:
_Unwind_RaiseException failed: _URC_FATAL_PHASE1_ERROR

For reference, here's the IR for suspend_coro with #[inline(never)]. As expected, the asm call
contains the unwind attribute. This function requires no cleanup, so no personality nor landing pad
are created. Unwinding proceeds normally through its corresponding stack frame.

; coro::arch::imp::suspend_coro
; Function Attrs: noinline nonlazybind uwtable
define void @_RNvNtNtCscfDV7XxA4Im_4coro4arch3imp12suspend_coro(ptr noundef nonnull align 8 %active) unnamed_addr #5 !guid !81 {
start:
  %_4 = load ptr, ptr %active, align 8, !nonnull !64, !noundef !64
  %0 = tail call { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float } asm sideeffect alignstack inteldialect unwind "push rbx\0Apush rbp\0Alea rax, [rip + 2f]\0Apush rax\0Amov [rdi + 0], rsp\0Amov rsp, rsi\0Apop rbp\0A.cfi_escape 0x2e, 0\0Aret\0A2:\0Apush rbp\0Amov [rdi + 0], rsp\0Alea rsp, [rsi + 8]\0Apop rbp\0Apop rbx", "={r12},={r13},={r14},={r15},={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},{di},{si},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{tmm0},~{tmm1},~{tmm2},~{tmm3},~{tmm4},~{tmm5},~{tmm6},~{tmm7},~{dirflag},~{fpsr},~{flags},~{memory}"(ptr nonnull %active, ptr nonnull %_4), !srcloc !82
  ret void
}

But when #[inline(always)], the IR for the inlinee function control_transfers::resume_from_different_coros::{closure#1} is:

; control_transfers::resume_from_different_coros::{closure#1}
; Function Attrs: inlinehint nonlazybind uwtable
define internal fastcc void @_RNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coross_0B3_(ptr nofree noundef nonnull align 8 captures(none) %_1) unnamed_addr #6 personality ptr @rust_eh_personality !guid !586 {
start:
  %_3 = alloca [8 x i8], align 4
  call void @llvm.lifetime.start.p0(ptr nonnull %_3)
  %_18 = load i64, ptr %_1, align 8, !noundef !132
  %0 = icmp eq i64 %_18, 0
  br i1 %0, label %bb14, label %bb13, !prof !560

bb14:                                             ; preds = %start
  store i64 -1, ptr %_1, align 8
  %_24 = getelementptr inbounds nuw i8, ptr %_1, i64 8
; invoke <coro::Coro<i32, control_transfers::resume_after_move::{closure#0}>>::resume
  %1 = invoke fastcc { i32, i32 } @_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_17resume_after_move0E6resumeBE_(ptr noalias nofree noundef align 8 dereferenceable(24) %_24)
          to label %bb1 unwind label %cleanup

bb13:                                             ; preds = %start
; call core::cell::panic_already_borrowed
  tail call void @_RNvNtCsdN1ZzDs28Vk_4core4cell22panic_already_borrowed(ptr noalias nofree noundef readonly align 8 captures(address, read_provenance) dereferenceable(24) @alloc_f92ec3a5190f40a4f61d46926f400512) #25
  unreachable

cleanup:                                          ; preds = %bb4, %bb14
  %2 = landingpad { ptr, i32 }
          cleanup
  br label %bb12

bb1:                                              ; preds = %bb14
  %3 = extractvalue { i32, i32 } %1, 0
  %4 = extractvalue { i32, i32 } %1, 1
  store i32 %3, ptr %_3, align 4
  %5 = getelementptr inbounds nuw i8, ptr %_3, i64 4
  store i32 %4, ptr %5, align 4
  %_5.i = icmp eq i32 %3, 0
  br i1 %_5.i, label %bb3, label %bb4, !prof !487

bb4:                                              ; preds = %bb1
; invoke core::panicking::assert_failed::<core::ops::coroutine::CoroutineState<(), i32>, core::ops::coroutine::CoroutineState<(), i32>>
  invoke fastcc void @_RINvNtCsdN1ZzDs28Vk_4core9panicking13assert_failedINtNtNtB4_3ops9coroutine14CoroutineStateulEBM_ECshisOEaq0iZN_17control_transfers(ptr noalias nofree noundef readonly align 4 captures(address, read_provenance) dereferenceable(8) %_3, ptr noalias nofree noundef readonly align 4 captures(address, read_provenance) dereferenceable(8) @alloc_bd30ae0d503613008a9049c05daf1744, ptr noalias nofree noundef readonly align 8 captures(address, read_provenance) dereferenceable(24) @alloc_84d20907cd6dbcb746b7b2abf3c753d9) #27
          to label %unreachable unwind label %cleanup

bb3:                                              ; preds = %bb1
  %_2.i.i.i7 = load i64, ptr %_1, align 8, !noundef !132
  %_3.i.i.i8 = add i64 %_2.i.i.i7, 1
  store i64 %_3.i.i.i8, ptr %_1, align 8
  call void @llvm.lifetime.end.p0(ptr nonnull %_3)
  %_11.i = icmp ult i64 %_3.i.i.i8, 9223372036854775807
  br i1 %_11.i, label %_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit, label %bb2.i9, !prof !560

bb2.i9:                                           ; preds = %bb3
; call core::cell::panic_already_mutably_borrowed
  tail call void @_RNvNtCsdN1ZzDs28Vk_4core4cell30panic_already_mutably_borrowed(ptr noalias nofree noundef nonnull readonly align 8 captures(address, read_provenance) dereferenceable(24) @alloc_178b316d1232c191ec98be2b6cd5cb4e) #25
  unreachable

_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit: ; preds = %bb3
  %b.i = add nsw i64 %_2.i.i.i7, 2
  store i64 %b.i, ptr %_1, align 8, !noalias !587
  tail call void @llvm.experimental.noalias.scope.decl(metadata !590)
  tail call void @llvm.experimental.noalias.scope.decl(metadata !593)
  %_8.i.i = load ptr, ptr %_24, align 8, !alias.scope !596, !nonnull !132, !noundef !132
  %_6.i.i = load ptr, ptr %_8.i.i, align 16, !noalias !596, !nonnull !132, !noundef !132
  %6 = ptrtoint ptr %_6.i.i to i64
  %7 = getelementptr inbounds nuw i8, ptr %_1, i64 16
  %_14.i.i = load ptr, ptr %7, align 8, !alias.scope !596, !nonnull !132, !noundef !132
  %8 = ptrtoint ptr %_14.i.i to i64
  %_9.i.i = sub i64 %6, %8
  %9 = getelementptr inbounds nuw i8, ptr %_1, i64 24
  %_16.i.i = load i64, ptr %9, align 8, !range !159, !alias.scope !596, !noundef !132
  %_5.not.i.i = icmp ult i64 %_9.i.i, %_16.i.i
  br i1 %_5.not.i.i, label %_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i, label %bb6.i.invoke

_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i: ; preds = %_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit
  %_3.i.i = load ptr, ptr %_6.i.i, align 8, !noalias !596, !noundef !132
  %.not.i = icmp eq ptr %_3.i.i, getelementptr inbounds nuw (i8, ptr @_RINvNtNtCscfDV7XxA4Im_4coro4arch3imp11first_tramplNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EBQ_, i64 1)
  br i1 %.not.i, label %bb6.i.invoke, label %bb2.i11, !prof !160

bb2.i11:                                          ; preds = %_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i
  %10 = getelementptr inbounds nuw i8, ptr %_8.i.i, i64 24
  %_4.i.i = load ptr, ptr %10, align 8, !noalias !597, !noundef !132
  %_3.i3.i = icmp eq ptr %_4.i.i, null
  br i1 %_3.i3.i, label %bb7, label %bb6.i.invoke, !prof !487

bb6.i.invoke:                                     ; preds = %_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit, %bb2.i11, %_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i
  %11 = phi ptr [ @alloc_a90b2cdc5510238b0390717c26dbfd52, %_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i ], [ @alloc_fa70cf512e51213f6e93f0d251b72a17, %bb2.i11 ], [ @alloc_fa70cf512e51213f6e93f0d251b72a17, %_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit ]
  %12 = phi i64 [ 36, %_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i ], [ 34, %bb2.i11 ], [ 34, %_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit ]
  %13 = phi ptr [ @alloc_d8aa9af72d2367601b2f42a76ca5794b, %_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i ], [ @alloc_99d67c30cdcd95a0cea586fa7474d0f1, %bb2.i11 ], [ @alloc_99d67c30cdcd95a0cea586fa7474d0f1, %_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit ]
; invoke core::panicking::panic
  invoke void @_RNvNtCsdN1ZzDs28Vk_4core9panicking5panic(ptr noalias nofree noundef nonnull readonly captures(address, read_provenance) %11, i64 noundef %12, ptr noalias nofree noundef readonly align 8 captures(address, read_provenance) dereferenceable(24) %13) #25
          to label %bb6.i.cont unwind label %cleanup1

bb6.i.cont:                                       ; preds = %bb6.i.invoke
  unreachable

unreachable:                                      ; preds = %bb4
  unreachable

cleanup1:                                         ; preds = %bb6.i.invoke
  %14 = landingpad { ptr, i32 }
          cleanup
  br label %bb12

bb7:                                              ; preds = %bb2.i11
  store i64 %_3.i.i.i8, ptr %_1, align 8
  %_3.i = tail call align 8 ptr @llvm.threadlocal.address.p0(ptr @_RNvCscfDV7XxA4Im_4coro6ACTIVE)
  %_2.i = load ptr, ptr %_3.i, align 8, !noundef !132
  %_9.i = icmp eq ptr %_2.i, null
  br i1 %_9.i, label %bb2.i, label %_RNvCscfDV7XxA4Im_4coro7suspend.exit, !prof !440

bb2.i:                                            ; preds = %bb7
; call coro::suspend_from_outside
  tail call void @_RNvCscfDV7XxA4Im_4coro20suspend_from_outside() #27
  unreachable

_RNvCscfDV7XxA4Im_4coro7suspend.exit:             ; preds = %bb7
  %_14.i = load ptr, ptr %_2.i, align 8, !nonnull !132, !noundef !132
  %15 = tail call { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float } asm sideeffect alignstack inteldialect unwind "push rbx\0Apush rbp\0Alea rax, [rip + 2f]\0Apush rax\0Amov [rdi + 0], rsp\0Amov rsp, rsi\0Apop rbp\0A.cfi_escape 0x2e, 0\0Aret\0A2:\0Apush rbp\0Amov [rdi + 0], rsp\0Alea rsp, [rsi + 8]\0Apop rbp\0Apop rbx", "={r12},={r13},={r14},={r15},={ax},={cx},={dx},={si},={di},={r8},={r9},={r10},={r11},={xmm0},={xmm1},={xmm2},={xmm3},={xmm4},={xmm5},={xmm6},={xmm7},={xmm8},={xmm9},={xmm10},={xmm11},={xmm12},={xmm13},={xmm14},={xmm15},{di},{si},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{k0},~{k1},~{k2},~{k3},~{k4},~{k5},~{k6},~{k7},~{st},~{st(1)},~{st(2)},~{st(3)},~{st(4)},~{st(5)},~{st(6)},~{st(7)},~{tmm0},~{tmm1},~{tmm2},~{tmm3},~{tmm4},~{tmm5},~{tmm6},~{tmm7},~{dirflag},~{fpsr},~{flags},~{memory}"(ptr nonnull %_2.i, ptr nonnull %_14.i), !srcloc !478
  ret void

bb12:                                             ; preds = %cleanup1, %cleanup
  %.sink = phi i64 [ -1, %cleanup1 ], [ 1, %cleanup ]
  %.pn = phi { ptr, i32 } [ %14, %cleanup1 ], [ %2, %cleanup ]
  %_2.i.i.i13 = load i64, ptr %_1, align 8, !noundef !132
  %_3.i.i.i14 = add i64 %_2.i.i.i13, %.sink
  store i64 %_3.i.i.i14, ptr %_1, align 8
  resume { ptr, i32 } %.pn
}

The relevant asm call appears right at the end, after label _RNvCscfDV7XxA4Im_4coro7suspend.exit.
The complete .ll file appears in this Godbolt.

LLVM produces the following assembly (when passing -O2 --exception-model=dwarf -mtriple=x86_64-unknown-linux-gnu) for this function:

        .text
        .prefalign      4, .Lfunc_end55, nop    # -- Begin function control_transfers[c97a7856c43022a1]::resume_from_different_coros::{closure#1}
        .type   control_transfers[c97a7856c43022a1]::resume_from_different_coros::{closure#1},@function
control_transfers[c97a7856c43022a1]::resume_from_different_coros::{closure#1}: # @control_transfers[c97a7856c43022a1]::resume_from_different_coros::{closure#1}
.Lfunc_begin21:
        .cfi_startproc
        .cfi_personality 3, rust_eh_personality
        .cfi_lsda 3, .Lexception21
# %bb.0:                                # %start
        push    r15
        .cfi_def_cfa_offset 16
        push    r14
        .cfi_def_cfa_offset 24
        push    r13
        .cfi_def_cfa_offset 32
        push    r12
        .cfi_def_cfa_offset 40
        push    rbx
        .cfi_def_cfa_offset 48
        sub     rsp, 16
        .cfi_def_cfa_offset 64
        .cfi_offset rbx, -48
        .cfi_offset r12, -40
        .cfi_offset r13, -32
        .cfi_offset r14, -24
        .cfi_offset r15, -16
        cmp     qword ptr [rdi], 0
        jne     .LBB55_4
# %bb.1:                                # %bb14
        mov     rbx, rdi
        mov     qword ptr [rdi], -1
        add     rdi, 8
.Ltmp189:                               # EH_LABEL
        call    <coro[8eb5da1bc166bea0]::Coro<i32, control_transfers[c97a7856c43022a1]::resume_after_move::{closure#0}>>::resume
.Ltmp190:                               # EH_LABEL
# %bb.2:                                # %bb1
        mov     dword ptr [rsp + 8], eax
        mov     dword ptr [rsp + 12], edx
        test    eax, eax
        jne     .LBB55_3
# %bb.5:                                # %bb3
        mov     rcx, qword ptr [rbx]
        lea     rax, [rcx + 1]
        mov     qword ptr [rbx], rax
        movabs  rdx, 9223372036854775807
        cmp     rax, rdx
        jae     .LBB55_18
# %bb.6:                                # %_RNvMst_NtCsdN1ZzDs28Vk_4core4cellINtB5_7RefCellINtCscfDV7XxA4Im_4coro4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0EE6borrowB1f_.exit
        add     rcx, 2
        mov     qword ptr [rbx], rcx
        mov     rcx, qword ptr [rbx + 8]
        mov     r8, qword ptr [rcx]
        mov     r9, r8
        sub     r9, qword ptr [rbx + 16]
        mov     edx, offset .Lalloc_99d67c30cdcd95a0cea586fa7474d0f1
        mov     esi, 34
        mov     edi, offset .Lalloc_fa70cf512e51213f6e93f0d251b72a17
        cmp     r9, qword ptr [rbx + 24]
        jae     .LBB55_10
# %bb.7:                                # %_RNvMs0_CscfDV7XxA4Im_4coroINtB5_4CorolNCNvCshisOEaq0iZN_17control_transferss_27resume_from_different_coros0E11has_startedBE_.exit.i
        cmp     qword ptr [r8], offset coro[8eb5da1bc166bea0]::arch::imp::first_tramp::<i32, control_transfers[c97a7856c43022a1]::resume_from_different_coros::{closure#0}>+1
        je      .LBB55_8
# %bb.9:                                # %bb2.i11
        cmp     qword ptr [rcx + 24], 0
        jne     .LBB55_10
# %bb.14:                               # %bb7
        mov     qword ptr [rbx], rax
        mov     rax, qword ptr [rip + coro[8eb5da1bc166bea0]::ACTIVE@GOTTPOFF]
        mov     rdi, qword ptr fs:[rax]
        test    rdi, rdi
        je      .LBB55_19
# %bb.15:                               # %_RNvCscfDV7XxA4Im_4coro7suspend.exit
        mov     rsi, qword ptr [rdi]
        #APP

        push    rbx
        push    rbp
        lea     rax, [rip + .Ltmp197]
        push    rax
        mov     qword ptr [rdi], rsp
        mov     rsp, rsi
        pop     rbp
        .cfi_escape 0x2e, 0x00

        ret
.Ltmp197:
        push    rbp
        mov     qword ptr [rdi], rsp
        lea     rsp, [rsi + 8]
        pop     rbp
        pop     rbx

        #NO_APP
        add     rsp, 16
        .cfi_def_cfa_offset 48
        pop     rbx
        .cfi_def_cfa_offset 40
        pop     r12
        .cfi_def_cfa_offset 32
        pop     r13
        .cfi_def_cfa_offset 24
        pop     r14
        .cfi_def_cfa_offset 16
        pop     r15
        .cfi_def_cfa_offset 8
        ret
.LBB55_8:
        .cfi_def_cfa_offset 64
        mov     edx, offset .Lalloc_d8aa9af72d2367601b2f42a76ca5794b
        mov     esi, 36
        mov     edi, offset .Lalloc_a90b2cdc5510238b0390717c26dbfd52
.LBB55_10:                              # %bb6.i.invoke
.Ltmp194:                               # EH_LABEL
        call    qword ptr [rip + core[a0a1658e72bc1214]::panicking::panic@GOTPCREL]
.Ltmp195:                               # EH_LABEL
# %bb.11:                               # %bb6.i.cont
.LBB55_4:                               # %bb13
        mov     edi, offset .Lalloc_f92ec3a5190f40a4f61d46926f400512
        call    qword ptr [rip + core[a0a1658e72bc1214]::cell::panic_already_borrowed@GOTPCREL]
.LBB55_3:                               # %bb4
.Ltmp191:                               # EH_LABEL
        lea     rdi, [rsp + 8]
        mov     esi, offset .Lalloc_bd30ae0d503613008a9049c05daf1744
        mov     edx, offset .Lalloc_84d20907cd6dbcb746b7b2abf3c753d9
        call    core[a0a1658e72bc1214]::panicking::assert_failed::<core[a0a1658e72bc1214]::ops::coroutine::CoroutineState<(), i32>, core[a0a1658e72bc1214]::ops::coroutine::CoroutineState<(), i32>>
.Ltmp192:                               # EH_LABEL
# %bb.12:                               # %unreachable
.LBB55_18:                              # %bb2.i9
        mov     edi, offset .Lalloc_178b316d1232c191ec98be2b6cd5cb4e
        call    qword ptr [rip + core[a0a1658e72bc1214]::cell::panic_already_mutably_borrowed@GOTPCREL]
.LBB55_19:                              # %bb2.i
        call    qword ptr [rip + coro[8eb5da1bc166bea0]::suspend_from_outside@GOTPCREL]
.LBB55_16:                              # %cleanup
.Ltmp193:                               # EH_LABEL
        mov     ecx, 1
        jmp     .LBB55_17
.LBB55_13:                              # %cleanup1
.Ltmp196:                               # EH_LABEL
        mov     rcx, -1
.LBB55_17:                              # %bb12
        add     qword ptr [rbx], rcx
        mov     rdi, rax
        call    _Unwind_Resume@PLT
.Lfunc_end55:
        .size   control_transfers[c97a7856c43022a1]::resume_from_different_coros::{closure#1}, .Lfunc_end55-control_transfers[c97a7856c43022a1]::resume_from_different_coros::{closure#1}
        .cfi_endproc
        .section        .gcc_except_table,"a",@progbits
        .p2align        2, 0x0
GCC_except_table55:
.Lexception21:
        .byte   255                             # @LPStart Encoding = omit
        .byte   255                             # @TType Encoding = omit
        .byte   1                               # Call site Encoding = uleb128
        .uleb128 .Lcst_end21-.Lcst_begin21
.Lcst_begin21:
        .uleb128 .Ltmp189-.Lfunc_begin21        # >> Call Site 1 <<
        .uleb128 .Ltmp190-.Ltmp189              #   Call between .Ltmp189 and .Ltmp190
        .uleb128 .Ltmp193-.Lfunc_begin21        #     jumps to .Ltmp193
        .byte   0                               #   On action: cleanup
        .uleb128 .Ltmp194-.Lfunc_begin21        # >> Call Site 2 <<
        .uleb128 .Ltmp195-.Ltmp194              #   Call between .Ltmp194 and .Ltmp195
        .uleb128 .Ltmp196-.Lfunc_begin21        #     jumps to .Ltmp196
        .byte   0                               #   On action: cleanup
        .uleb128 .Ltmp195-.Lfunc_begin21        # >> Call Site 3 <<
        .uleb128 .Ltmp191-.Ltmp195              #   Call between .Ltmp195 and .Ltmp191
        .byte   0                               #     has no landing pad
        .byte   0                               #   On action: cleanup
        .uleb128 .Ltmp191-.Lfunc_begin21        # >> Call Site 4 <<
        .uleb128 .Ltmp192-.Ltmp191              #   Call between .Ltmp191 and .Ltmp192
        .uleb128 .Ltmp193-.Lfunc_begin21        #     jumps to .Ltmp193
        .byte   0                               #   On action: cleanup
        .uleb128 .Ltmp192-.Lfunc_begin21        # >> Call Site 5 <<
        .uleb128 .Lfunc_end55-.Ltmp192          #   Call between .Ltmp192 and .Lfunc_end55
        .byte   0                               #     has no landing pad
        .byte   0                               #   On action: cleanup
.Lcst_end21:
        .p2align        2, 0x0
                                        # -- End function

Notice in particular that there's no call site info for the asm! instructions in between
the #APP and #NO_APP comments, i.e. no call site entry between .Ltmp197 and .Ltmp194.

Ideally I'd have liked to provide a small reproducer, but I've been unable to hit all the
necessary conditions with only a few lines of code. In fact, I'm not sure if inlining even
plays a role in the actual issue. I can keep working on it during the next few days if it
would facilitate things. I can also make the coroutine crate repo public so you can inspect
it.
See the next comment. Ultimately I don't know if this is a Rust or a LLVM codegen issue, so I decided to
open the issue here first. Also, I'm not 100% sure whether unwinding in this way is allowed.

@Amanieu do you think this issue could be related to the crashes you observed here?

Meta

rustc --version --verbose:

rustc 1.100.0-nightly (8fa1c96cf 2026-08-17)
binary: rustc
commit-hash: 8fa1c96cfd489e4c27654c144ae871ce2c4db6c6
commit-date: 2026-08-17
host: x86_64-unknown-linux-gnu
release: 1.100.0-nightly
LLVM version: 23.1.0

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/sys/personality/dwarf/eh.rs, especially find_eh_action, and compare the provided inline(never) and inline(always) LLVM IR for asm! with may_unwind. Use the smaller reproducer from the issue and inspect the emitted LSDA call-site information. Done means unwinding can locate the cleanup for an instruction inside an inlined asm! block.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.