Skip to content

Stack space not reused  #85230

@jrmuizel

Description

@jrmuizel

Given this code:

struct Obj([u8; 8]);

extern "C" { fn f(o: &Obj); }

pub unsafe fn main()
{
    let s = Obj([1,2,3,4,5,6,7,8]);
    f(&s);
    std::mem::drop(s);
    
    let r = Obj([11,12,13,14,15,16,17,18]);
    f(&r);
    std::mem::drop(r);
}

It compiles to:

example::main:
        push    rbx
        sub     rsp, 16
        movabs  rax, 578437695752307201
        mov     qword ptr [rsp], rax
        mov     rbx, qword ptr [rip + f@GOTPCREL]
        mov     rdi, rsp
        call    rbx
        movabs  rax, 1301839424133073931
        mov     qword ptr [rsp + 8], rax
        lea     rdi, [rsp + 8]
        call    rbx
        add     rsp, 16
        pop     rbx
        ret

The first value is stored at [rsp] and the second value at [rsp + 8]

The LLVM IR looks like:

; example::main
; Function Attrs: nounwind nonlazybind uwtable
define void @_ZN7example4main17h377fad6dc64462c1E() unnamed_addr #0 !dbg !6 {
start:
  %r = alloca %Obj, align 1
  %s = alloca %Obj, align 1
  %0 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 0, i64 0, !dbg !10
  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0), !dbg !10
  store i8 1, i8* %0, align 1, !dbg !11
  %1 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 1, i64 1, !dbg !11
  store i8 2, i8* %1, align 1, !dbg !11
  %2 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 1, i64 2, !dbg !11
  store i8 3, i8* %2, align 1, !dbg !11
  %3 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 1, i64 3, !dbg !11
  store i8 4, i8* %3, align 1, !dbg !11
  %4 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 1, i64 4, !dbg !11
  store i8 5, i8* %4, align 1, !dbg !11
  %5 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 1, i64 5, !dbg !11
  store i8 6, i8* %5, align 1, !dbg !11
  %6 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 1, i64 6, !dbg !11
  store i8 7, i8* %6, align 1, !dbg !11
  %7 = getelementptr inbounds %Obj, %Obj* %s, i64 0, i32 1, i64 7, !dbg !11
  store i8 8, i8* %7, align 1, !dbg !11
  call void @f(%Obj* noalias nonnull readonly align 1 dereferenceable(8) %s), !dbg !12
  %8 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 0, i64 0, !dbg !13
  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %8), !dbg !13
  store i8 11, i8* %8, align 1, !dbg !14
  %9 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 1, i64 1, !dbg !14
  store i8 12, i8* %9, align 1, !dbg !14
  %10 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 1, i64 2, !dbg !14
  store i8 13, i8* %10, align 1, !dbg !14
  %11 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 1, i64 3, !dbg !14
  store i8 14, i8* %11, align 1, !dbg !14
  %12 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 1, i64 4, !dbg !14
  store i8 15, i8* %12, align 1, !dbg !14
  %13 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 1, i64 5, !dbg !14
  store i8 16, i8* %13, align 1, !dbg !14
  %14 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 1, i64 6, !dbg !14
  store i8 17, i8* %14, align 1, !dbg !14
  %15 = getelementptr inbounds %Obj, %Obj* %r, i64 0, i32 1, i64 7, !dbg !14
  store i8 18, i8* %15, align 1, !dbg !14
  call void @f(%Obj* noalias nonnull readonly align 1 dereferenceable(8) %r), !dbg !15
  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %8), !dbg !16
  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0), !dbg !16
  ret void, !dbg !17
}

The llvm.lifetime.end intrinsic for the first value shows up at the end instead of after the call to drop. If I move it earlier then LLVM does reuse the stack slot.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-codegenArea: Code generationC-bugCategory: This is a bug.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