Skip to content

rustc emits unnecessary cleanup blocks for no-drop types #159399

Description

@hanna-kruppe

I tried this code (abstracted from #159302, compiler explorer):

#[inline(never)]
fn inner(_: &dyn Sync) {}

fn wrapper<T: Sync>(val: T) {
    inner(&val);
}

#[inline(never)]
pub fn wrapper_u32() {
    wrapper(1u32);
}

#[inline(never)]
pub fn wrapper_u32_manual(x: u32) {
    inner(&x);
}

I expected to see this happen: Even without/before LLVM optimizations, the generated code for wrapper::<u32> is the same as for wrapper_u32_manual. Both just stash their argument on the stack and call inner with a pointer to that stack slot and the vtable pointer.

Instead, this happened: The code that rustc emits for wrapper::<u32> has unnecessary cleanup blocks. In the final assembly that's everything after the ret:

example[24ebc4c2b62952c4]::wrapper::<u32>:
        sub     rsp, 24
        mov     dword ptr [rsp + 4], edi
        lea     rsi, [rip + .Lanon.76072c6d33abe03aa460b47b2020a892.0]
        lea     rdi, [rsp + 4]
        call    example[24ebc4c2b62952c4]::inner
        add     rsp, 24
        ret
        mov     qword ptr [rsp + 8], rax
        mov     dword ptr [rsp + 16], edx
        mov     rdi, qword ptr [rsp + 8]
        call    _Unwind_Resume@PLT

It's due to this part of the LLVM IR that rustc emits:

; ...
  invoke void @example[24ebc4c2b62952c4]::inner(ptr noundef nonnull %val, ptr noalias nofree noundef readonly align 8 captures(address, read_provenance) dereferenceable(24) @vtable.0)
          to label %bb1 unwind label %cleanup

bb3:
  %2 = load ptr, ptr %1, align 8
  %3 = getelementptr inbounds i8, ptr %1, i64 8
  %4 = load i32, ptr %3, align 8
  call void @llvm.lifetime.end.p0(ptr %1)
  %5 = insertvalue { ptr, i32 } poison, ptr %2, 0
  %6 = insertvalue { ptr, i32 } %5, i32 %4, 1
  resume { ptr, i32 } %6

cleanup:
  %7 = landingpad { ptr, i32 }
          cleanup
  %8 = extractvalue { ptr, i32 } %7, 0
  %9 = extractvalue { ptr, i32 } %7, 1
  call void @llvm.lifetime.start.p0(ptr %1)
  store ptr %8, ptr %1, align 8
  %10 = getelementptr inbounds i8, ptr %1, i64 8
  store i32 %9, ptr %10, align 8
  br label %bb3

Of course, LLVM optimization passes eventually clean it up, but this is probably wasting some compile time, and also hurts code size in debug builds without optimizations.

Meta

rustc --version --verbose (according to compiler explorer UI):

rustc 1.99.0-nightly (d0babd8b6 2026-07-15)
binary: rustc
commit-hash: d0babd8b6b05ef9bb65d42f928cef4129d64cf65
commit-date: 2026-07-15
host: x86_64-unknown-linux-gnu
release: 1.99.0-nightly
LLVM version: 22.1.8
Internal compiler ID: nightly

Metadata

Metadata

Labels

A-codegenArea: Code generationC-bugCategory: This is a bug.I-compiletimeIssue: Problems and improvements with respect to compile times.I-heavyIssue: Problems and improvements with respect to binary size of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions