Skip to content

Commit 7425fb2

Browse files
committed
Auto merge of rust-lang#98377 - davidv1992:add-lifetimes-to-argument-temporaries, r=oli-obk
Added llvm lifetime annotations to function call argument temporaries. The goal of this change is to ensure that llvm will do stack slot optimization on these temporaries. This ensures that in code like: ```rust const A: [u8; 1024] = [0; 1024]; fn copy_const() { f(A); f(A); } ``` we only use 1024 bytes of stack space, instead of 2048 bytes. I am new to developing for the rust compiler, and as such not entirely sure, but I believe this should be sufficient to close rust-lang#98156. Also, this does not contain a test case to ensure this keeps working, primarily because I am not sure how to go about testing this. I would love some suggestions as to how that could be approached.
2 parents 7b68106 + 259a7a7 commit 7425fb2

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
132132
llargs: &[Bx::Value],
133133
destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
134134
cleanup: Option<mir::BasicBlock>,
135+
copied_constant_arguments: &[PlaceRef<'tcx, <Bx as BackendTypes>::Value>],
135136
) {
136137
// If there is a cleanup block and the function we're calling can unwind, then
137138
// do an invoke, otherwise do a call.
@@ -172,6 +173,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
172173
if let Some((ret_dest, target)) = destination {
173174
bx.switch_to_block(fx.llbb(target));
174175
fx.set_debug_loc(bx, self.terminator.source_info);
176+
for tmp in copied_constant_arguments {
177+
bx.lifetime_end(tmp.llval, tmp.layout.size);
178+
}
175179
fx.store_return(bx, ret_dest, &fn_abi.ret, invokeret);
176180
}
177181
} else {
@@ -186,6 +190,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
186190
}
187191

188192
if let Some((ret_dest, target)) = destination {
193+
for tmp in copied_constant_arguments {
194+
bx.lifetime_end(tmp.llval, tmp.layout.size);
195+
}
189196
fx.store_return(bx, ret_dest, &fn_abi.ret, llret);
190197
self.funclet_br(fx, bx, target);
191198
} else {
@@ -415,6 +422,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
415422
args,
416423
Some((ReturnDest::Nothing, target)),
417424
unwind,
425+
&[],
418426
);
419427
}
420428

@@ -492,7 +500,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
492500
let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), lang_item);
493501

494502
// Codegen the actual panic invoke/call.
495-
helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup);
503+
helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup, &[]);
496504
}
497505

498506
fn codegen_abort_terminator(
@@ -508,7 +516,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
508516
let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), LangItem::PanicNoUnwind);
509517

510518
// Codegen the actual panic invoke/call.
511-
helper.do_call(self, &mut bx, fn_abi, llfn, &[], None, None);
519+
helper.do_call(self, &mut bx, fn_abi, llfn, &[], None, None, &[]);
512520
}
513521

514522
/// Returns `true` if this is indeed a panic intrinsic and codegen is done.
@@ -579,6 +587,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
579587
&[msg.0, msg.1, location],
580588
target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)),
581589
cleanup,
590+
&[],
582591
);
583592
} else {
584593
// a NOP
@@ -786,6 +795,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
786795
(args, None)
787796
};
788797

798+
let mut copied_constant_arguments = vec![];
789799
'make_args: for (i, arg) in first_args.iter().enumerate() {
790800
let mut op = self.codegen_operand(&mut bx, arg);
791801

@@ -851,8 +861,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
851861
(&mir::Operand::Copy(_), Ref(_, None, _))
852862
| (&mir::Operand::Constant(_), Ref(_, None, _)) => {
853863
let tmp = PlaceRef::alloca(&mut bx, op.layout);
864+
bx.lifetime_start(tmp.llval, tmp.layout.size);
854865
op.val.store(&mut bx, tmp);
855866
op.val = Ref(tmp.llval, None, tmp.align);
867+
copied_constant_arguments.push(tmp);
856868
}
857869
_ => {}
858870
}
@@ -925,6 +937,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
925937
&llargs,
926938
target.as_ref().map(|&target| (ret_dest, target)),
927939
cleanup,
940+
&copied_constant_arguments,
928941
);
929942

930943
bx.switch_to_block(bb_fail);
@@ -942,6 +955,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
942955
&llargs,
943956
target.as_ref().map(|&target| (ret_dest, target)),
944957
cleanup,
958+
&copied_constant_arguments,
945959
);
946960
}
947961

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// This test checks that temporaries for indirectly-passed arguments get lifetime markers.
2+
3+
// compile-flags: -O -C no-prepopulate-passes -Zmir-opt-level=0
4+
5+
#![crate_type = "lib"]
6+
7+
extern "Rust" {
8+
fn f(x: [u8; 1024]);
9+
}
10+
11+
const A: [u8; 1024] = [0; 1024];
12+
13+
// CHECK-LABEL: @const_arg_indirect
14+
#[no_mangle]
15+
pub unsafe fn const_arg_indirect() {
16+
// Ensure that the live ranges for the two argument temporaries don't overlap.
17+
18+
// CHECK: call void @llvm.lifetime.start
19+
// CHECK: call void @f
20+
// CHECK: call void @llvm.lifetime.end
21+
// CHECK: call void @llvm.lifetime.start
22+
// CHECK: call void @f
23+
// CHECK: call void @llvm.lifetime.end
24+
25+
f(A);
26+
f(A);
27+
}

0 commit comments

Comments
 (0)