Stack slots are not being reused due to missing LLVM IR lifetime annotations.
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the provided reproducer and compare the generated LLVM IR from rustc 1.91 against 1.92 or newer, focusing on the missing llvm.lifetime.start and llvm.lifetime.end calls. Confirm whether the change explains the increased stack allocation, then identify the compiler regression and verify that the lifetime annotations and stack-slot reuse are restored without changing the reported behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100