Skip to content

Stack slots are not being reused due to missing LLVM IR lifetime annotations. #157676

@VinceQu

Description

@VinceQu

Code

I tried this code:

static STRUCT_SIZE : usize = 1024;

struct LargeStruct {
    data: [u8; STRUCT_SIZE],
}

#[inline(never)]
fn consume_large_struct(val: LargeStruct) -> usize {
    println!("consumed val[0] = {}", val.data[0]);
    val.data[0] as usize
}

#[unsafe(no_mangle)]
pub fn main(decision : i64) {
    let mut a = LargeStruct { data: [0u8; STRUCT_SIZE] };
    a.data[0] = 1;
    consume_large_struct(a); // `a` is moved here 

    if decision > 0 { // Branch to prevent initialization reordering
        let mut b = LargeStruct { data: [0u8; STRUCT_SIZE] };
        b.data[0] = 2;
        consume_large_struct(b); // `b` is moved here
    }
}

I expected to see this happen: In rustc versions < 1.92 the stack slot occupied by a gets reused for b because a's lifetime ended.

Instead, this happened: In rustc versions >= 1.92 this behavior is no longer observed and the function reserves stack space for both a and b.

See https://godbolt.org/z/o4Ye3aKP3 for the expected behavior: sub rsp, 1064
See https://godbolt.org/z/erPebqsdP for the unexpected behavior: sub rsp, 2088

It appears that the lifetime annotations (call void @llvm.lifetime.start.p0(i64 1024, ptr nonnull %_8)) are missing from the LLVM IR for the newer rustc versions. I am not sure if this is intended behavior due to a newly introduced optimization pass, just wanted to report it in case it was an unwanted side effect.

LLVM IR for 1.92:

define void @example::main::hbaa888d9139c15b4() unnamed_addr {
start:
  %b = alloca [1024 x i8], align 1
  %a = alloca [1024 x i8], align 1
  %0 = getelementptr inbounds nuw i8, ptr %a, i64 1
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1023) %0, i8 0, i64 1023, i1 false)
  store i8 1, ptr %a, align 1
  %res = call fastcc noundef i64 @example::consume_large_struct::h22f578e03999bc66(ptr noalias noundef readonly align 1 captures(address) dereferenceable(1024) %a) #4
  %_4.not = icmp eq i64 %res, 0
  br i1 %_4.not, label %bb4, label %bb2

bb4:
  ret void

bb2:
  %1 = getelementptr inbounds nuw i8, ptr %b, i64 1
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1023) %1, i8 0, i64 1023, i1 false)
  store i8 2, ptr %b, align 1
  %_6 = call fastcc noundef i64 @example::consume_large_struct::h22f578e03999bc66(ptr noalias noundef readonly align 1 captures(address) dereferenceable(1024) %b) #4
  br label %bb4
}

LLVM IR for 1.91


define void @example::main::hf6eacbf6b45cf567() unnamed_addr {
start:
  %_8 = alloca [1024 x i8], align 1
  %_4 = alloca [1024 x i8], align 1
  %a.sroa.5.0._4.sroa_idx = getelementptr inbounds nuw i8, ptr %_4, i64 1
  call void @llvm.lifetime.start.p0(i64 1024, ptr nonnull %_4)
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1023) %a.sroa.5.0._4.sroa_idx, i8 0, i64 1023, i1 false)
  store i8 1, ptr %_4, align 1
  %res = call fastcc noundef i64 @example::consume_large_struct::h7e10161d3a176006(ptr noalias noundef readonly align 1 captures(address) dereferenceable(1024) %_4) #4
  call void @llvm.lifetime.end.p0(i64 1024, ptr nonnull %_4)
  %_5.not = icmp eq i64 %res, 0
  br i1 %_5.not, label %bb4, label %bb2

bb4:
  ret void

bb2:
  %b.sroa.5.0._8.sroa_idx = getelementptr inbounds nuw i8, ptr %_8, i64 1
  call void @llvm.lifetime.start.p0(i64 1024, ptr nonnull %_8)
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1023) %b.sroa.5.0._8.sroa_idx, i8 0, i64 1023, i1 false)
  store i8 2, ptr %_8, align 1
  %_7 = call fastcc noundef i64 @example::consume_large_struct::h7e10161d3a176006(ptr noalias noundef readonly align 1 captures(address) dereferenceable(1024) %_8) #4
  call void @llvm.lifetime.end.p0(i64 1024, ptr nonnull %_8)
  br label %bb4
}

Version it worked on

It most recently worked on: rustc 1.91

Version with regression

Everything from rustc 1.92 up to current beta/nightly

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-mir-optArea: MIR optimizationsC-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}regression-untriagedUntriaged performance or correctness regression.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions