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
Code
I tried this code:
I expected to see this happen: In rustc versions < 1.92 the stack slot occupied by
agets reused forbbecausea'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
aandb.See https://godbolt.org/z/o4Ye3aKP3 for the expected behavior:
sub rsp, 1064See https://godbolt.org/z/erPebqsdP for the unexpected behavior:
sub rsp, 2088It 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:
LLVM IR for 1.91
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