Skip to content

Commit b0b80f8

Browse files
committed
Auto merge of #45380 - dotdash:arg_copies, r=arielb1
Avoid unnecessary copies of arguments that are simple bindings Initially MIR differentiated between arguments and locals, which introduced a need to add extra copies assigning the argument to a local, even for simple bindings. This differentiation no longer exists, but we're still creating those copies, bloating the MIR and LLVM IR we emit. Additionally, the current approach means that we create debug info for both the incoming argument (marking it as an argument), and then immediately shadow it a local that goes by the same name. This can be confusing when using e.g. "info args" in gdb, or when e.g. a debugger with a GUI displays the function arguments separately from the local variables, especially when the binding is mutable, because the argument doesn't change, while the local variable does.
2 parents e0febe7 + 8ad7c28 commit b0b80f8

21 files changed

+145
-122
lines changed

src/librustc/mir/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,9 @@ pub enum TerminatorKind<'tcx> {
650650
Call {
651651
/// The function that’s being called
652652
func: Operand<'tcx>,
653-
/// Arguments the function is called with
653+
/// Arguments the function is called with. These are owned by the callee, which is free to
654+
/// modify them. This is important as "by-value" arguments might be passed by-reference at
655+
/// the ABI level.
654656
args: Vec<Operand<'tcx>>,
655657
/// Destination for the return value. If some, the call is converging.
656658
destination: Option<(Lvalue<'tcx>, BasicBlock)>,

src/librustc_mir/build/expr/into.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
247247
} else {
248248
let args: Vec<_> =
249249
args.into_iter()
250-
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
250+
.map(|arg| {
251+
let scope = this.local_scope();
252+
// Function arguments are owned by the callee, so we need as_temp()
253+
// instead of as_operand() to enforce copies
254+
let operand = unpack!(block = this.as_temp(block, scope, arg));
255+
Operand::Consume(Lvalue::Local(operand))
256+
})
251257
.collect();
252258

253259
let success = this.cfg.start_new_block();

src/librustc_mir/build/mod.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc::mir::visit::{MutVisitor, Lookup};
2121
use rustc::ty::{self, Ty, TyCtxt};
2222
use rustc::ty::subst::Substs;
2323
use rustc::util::nodemap::NodeMap;
24+
use rustc_const_eval::pattern::{BindingMode, PatternKind};
2425
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
2526
use shim;
2627
use std::mem;
@@ -571,13 +572,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
571572
// Bind the argument patterns
572573
for (index, &(ty, pattern)) in arguments.iter().enumerate() {
573574
// Function arguments always get the first Local indices after the return pointer
574-
let lvalue = Lvalue::Local(Local::new(index + 1));
575+
let local = Local::new(index + 1);
576+
let lvalue = Lvalue::Local(local);
575577

576578
if let Some(pattern) = pattern {
577579
let pattern = self.hir.pattern_from_hir(pattern);
578-
scope = self.declare_bindings(scope, ast_body.span,
579-
LintLevel::Inherited, &pattern);
580-
unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue));
580+
581+
match *pattern.kind {
582+
// Don't introduce extra copies for simple bindings
583+
PatternKind::Binding { mutability, var, mode: BindingMode::ByValue, .. } => {
584+
self.local_decls[local].mutability = mutability;
585+
self.var_indices.insert(var, local);
586+
}
587+
_ => {
588+
scope = self.declare_bindings(scope, ast_body.span,
589+
LintLevel::Inherited, &pattern);
590+
unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue));
591+
}
592+
}
581593
}
582594

583595
// Make sure we drop (parts of) the argument even when not matched on.

src/librustc_trans/builder.rs

+7
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
112112
}
113113
}
114114

115+
pub fn set_value_name(&self, value: ValueRef, name: &str) {
116+
let cname = CString::new(name.as_bytes()).unwrap();
117+
unsafe {
118+
llvm::LLVMSetValueName(value, cname.as_ptr());
119+
}
120+
}
121+
115122
pub fn position_before(&self, insn: ValueRef) {
116123
unsafe {
117124
llvm::LLVMPositionBuilderBefore(self.llbuilder, insn);

src/librustc_trans/mir/analyze.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,18 @@ struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> {
6464

6565
impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> {
6666
fn new(mircx: &'mir MirContext<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> {
67-
LocalAnalyzer {
67+
let mut analyzer = LocalAnalyzer {
6868
cx: mircx,
6969
lvalue_locals: BitVector::new(mircx.mir.local_decls.len()),
7070
seen_assigned: BitVector::new(mircx.mir.local_decls.len())
71+
};
72+
73+
// Arguments get assigned to by means of the function being called
74+
for idx in 0..mircx.mir.arg_count {
75+
analyzer.seen_assigned.insert(idx + 1);
7176
}
77+
78+
analyzer
7279
}
7380

7481
fn mark_as_lvalue(&mut self, local: mir::Local) {

src/librustc_trans/mir/mod.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,12 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
386386
let arg_decl = &mir.local_decls[local];
387387
let arg_ty = mircx.monomorphize(&arg_decl.ty);
388388

389+
let name = if let Some(name) = arg_decl.name {
390+
name.as_str().to_string()
391+
} else {
392+
format!("arg{}", arg_index)
393+
};
394+
389395
if Some(local) == mir.spread_arg {
390396
// This argument (e.g. the last argument in the "rust-call" ABI)
391397
// is a tuple that was spread at the ABI level and now we have
@@ -397,7 +403,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
397403
_ => bug!("spread argument isn't a tuple?!")
398404
};
399405

400-
let lvalue = LvalueRef::alloca(bcx, arg_ty, &format!("arg{}", arg_index));
406+
let lvalue = LvalueRef::alloca(bcx, arg_ty, &name);
401407
for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() {
402408
let (dst, _) = lvalue.trans_field_ptr(bcx, i);
403409
let arg = &mircx.fn_ty.args[idx];
@@ -444,6 +450,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
444450
llarg_idx += 1;
445451
}
446452
let llarg = llvm::get_param(bcx.llfn(), llarg_idx as c_uint);
453+
bcx.set_value_name(llarg, &name);
447454
llarg_idx += 1;
448455
llarg
449456
} else if !lvalue_locals.contains(local.index()) &&
@@ -481,10 +488,13 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
481488
let meta_llty = type_of::unsized_info_ty(bcx.ccx, pointee);
482489

483490
let llarg = bcx.pointercast(llarg, data_llty.ptr_to());
491+
bcx.set_value_name(llarg, &(name.clone() + ".ptr"));
484492
let llmeta = bcx.pointercast(llmeta, meta_llty);
493+
bcx.set_value_name(llmeta, &(name + ".meta"));
485494

486495
OperandValue::Pair(llarg, llmeta)
487496
} else {
497+
bcx.set_value_name(llarg, &name);
488498
OperandValue::Immediate(llarg)
489499
};
490500
let operand = OperandRef {
@@ -493,7 +503,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
493503
};
494504
return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
495505
} else {
496-
let lltemp = LvalueRef::alloca(bcx, arg_ty, &format!("arg{}", arg_index));
506+
let lltemp = LvalueRef::alloca(bcx, arg_ty, &name);
497507
if common::type_is_fat_ptr(bcx.ccx, arg_ty) {
498508
// we pass fat pointers as two words, but we want to
499509
// represent them internally as a pointer to two words,

src/test/codegen/adjustments.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#![crate_type = "lib"]
1414

1515
// Hack to get the correct size for the length part in slices
16-
// CHECK: @helper([[USIZE:i[0-9]+]])
16+
// CHECK: @helper([[USIZE:i[0-9]+]] %arg0)
1717
#[no_mangle]
1818
fn helper(_: usize) {
1919
}
@@ -23,9 +23,9 @@ fn helper(_: usize) {
2323
pub fn no_op_slice_adjustment(x: &[u8]) -> &[u8] {
2424
// We used to generate an extra alloca and memcpy for the block's trailing expression value, so
2525
// check that we copy directly to the return value slot
26-
// CHECK: %2 = insertvalue { i8*, [[USIZE]] } undef, i8* %0, 0
27-
// CHECK: %3 = insertvalue { i8*, [[USIZE]] } %2, [[USIZE]] %1, 1
28-
// CHECK: ret { i8*, [[USIZE]] } %3
26+
// CHECK: %0 = insertvalue { i8*, [[USIZE]] } undef, i8* %x.ptr, 0
27+
// CHECK: %1 = insertvalue { i8*, [[USIZE]] } %0, [[USIZE]] %x.meta, 1
28+
// CHECK: ret { i8*, [[USIZE]] } %1
2929
{ x }
3030
}
3131

src/test/codegen/align-struct.rs

-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pub fn align64(i : i32) -> Align64 {
4242
#[no_mangle]
4343
pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 {
4444
// CHECK: %n64 = alloca %Nested64, align 64
45-
// CHECK: %a = alloca %Align64, align 64
4645
let n64 = Nested64 { a, b, c, d };
4746
n64
4847
}
@@ -51,7 +50,6 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 {
5150
#[no_mangle]
5251
pub fn enum64(a: Align64) -> Enum64 {
5352
// CHECK: %e64 = alloca %Enum64, align 64
54-
// CHECK: %a = alloca %Align64, align 64
5553
let e64 = Enum64::A(a);
5654
e64
5755
}

src/test/codegen/fastcall-inreg.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -60,27 +60,27 @@
6060
#![crate_type = "lib"]
6161

6262
mod tests {
63-
// CHECK: @f1(i32 inreg, i32 inreg, i32)
63+
// CHECK: @f1(i32 inreg %arg0, i32 inreg %arg1, i32 %arg2)
6464
#[no_mangle]
6565
extern "fastcall" fn f1(_: i32, _: i32, _: i32) {}
6666

67-
// CHECK: @f2(i32* inreg, i32* inreg, i32*)
67+
// CHECK: @f2(i32* inreg %arg0, i32* inreg %arg1, i32* %arg2)
6868
#[no_mangle]
6969
extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {}
7070

71-
// CHECK: @f3(float, i32 inreg, i32 inreg, i32)
71+
// CHECK: @f3(float %arg0, i32 inreg %arg1, i32 inreg %arg2, i32 %arg3)
7272
#[no_mangle]
7373
extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {}
7474

75-
// CHECK: @f4(i32 inreg, float, i32 inreg, i32)
75+
// CHECK: @f4(i32 inreg %arg0, float %arg1, i32 inreg %arg2, i32 %arg3)
7676
#[no_mangle]
7777
extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {}
7878

79-
// CHECK: @f5(i64, i32)
79+
// CHECK: @f5(i64 %arg0, i32 %arg1)
8080
#[no_mangle]
8181
extern "fastcall" fn f5(_: i64, _: i32) {}
8282

83-
// CHECK: @f6(i1 inreg zeroext, i32 inreg, i32)
83+
// CHECK: @f6(i1 inreg zeroext %arg0, i32 inreg %arg1, i32 %arg2)
8484
#[no_mangle]
8585
extern "fastcall" fn f6(_: bool, _: i32, _: i32) {}
8686
}

src/test/codegen/function-arguments.rs

+16-16
Original file line numberDiff line numberDiff line change
@@ -21,62 +21,62 @@ pub struct UnsafeInner {
2121
_field: std::cell::UnsafeCell<i16>,
2222
}
2323

24-
// CHECK: zeroext i1 @boolean(i1 zeroext)
24+
// CHECK: zeroext i1 @boolean(i1 zeroext %x)
2525
#[no_mangle]
2626
pub fn boolean(x: bool) -> bool {
2727
x
2828
}
2929

30-
// CHECK: @readonly_borrow(i32* noalias readonly dereferenceable(4))
30+
// CHECK: @readonly_borrow(i32* noalias readonly dereferenceable(4) %arg0)
3131
// FIXME #25759 This should also have `nocapture`
3232
#[no_mangle]
3333
pub fn readonly_borrow(_: &i32) {
3434
}
3535

36-
// CHECK: @static_borrow(i32* noalias readonly dereferenceable(4))
36+
// CHECK: @static_borrow(i32* noalias readonly dereferenceable(4) %arg0)
3737
// static borrow may be captured
3838
#[no_mangle]
3939
pub fn static_borrow(_: &'static i32) {
4040
}
4141

42-
// CHECK: @named_borrow(i32* noalias readonly dereferenceable(4))
42+
// CHECK: @named_borrow(i32* noalias readonly dereferenceable(4) %arg0)
4343
// borrow with named lifetime may be captured
4444
#[no_mangle]
4545
pub fn named_borrow<'r>(_: &'r i32) {
4646
}
4747

48-
// CHECK: @unsafe_borrow(%UnsafeInner* dereferenceable(2))
48+
// CHECK: @unsafe_borrow(%UnsafeInner* dereferenceable(2) %arg0)
4949
// unsafe interior means this isn't actually readonly and there may be aliases ...
5050
#[no_mangle]
5151
pub fn unsafe_borrow(_: &UnsafeInner) {
5252
}
5353

54-
// CHECK: @mutable_unsafe_borrow(%UnsafeInner* dereferenceable(2))
54+
// CHECK: @mutable_unsafe_borrow(%UnsafeInner* dereferenceable(2) %arg0)
5555
// ... unless this is a mutable borrow, those never alias
5656
// ... except that there's this LLVM bug that forces us to not use noalias, see #29485
5757
#[no_mangle]
5858
pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {
5959
}
6060

61-
// CHECK: @mutable_borrow(i32* dereferenceable(4))
61+
// CHECK: @mutable_borrow(i32* dereferenceable(4) %arg0)
6262
// FIXME #25759 This should also have `nocapture`
6363
// ... there's this LLVM bug that forces us to not use noalias, see #29485
6464
#[no_mangle]
6565
pub fn mutable_borrow(_: &mut i32) {
6666
}
6767

68-
// CHECK: @indirect_struct(%S* noalias nocapture dereferenceable(32))
68+
// CHECK: @indirect_struct(%S* noalias nocapture dereferenceable(32) %arg0)
6969
#[no_mangle]
7070
pub fn indirect_struct(_: S) {
7171
}
7272

73-
// CHECK: @borrowed_struct(%S* noalias readonly dereferenceable(32))
73+
// CHECK: @borrowed_struct(%S* noalias readonly dereferenceable(32) %arg0)
7474
// FIXME #25759 This should also have `nocapture`
7575
#[no_mangle]
7676
pub fn borrowed_struct(_: &S) {
7777
}
7878

79-
// CHECK: noalias dereferenceable(4) i32* @_box(i32* noalias dereferenceable(4))
79+
// CHECK: noalias dereferenceable(4) i32* @_box(i32* noalias dereferenceable(4) %x)
8080
#[no_mangle]
8181
pub fn _box(x: Box<i32>) -> Box<i32> {
8282
x
@@ -91,31 +91,31 @@ pub fn struct_return() -> S {
9191
}
9292

9393
// Hack to get the correct size for the length part in slices
94-
// CHECK: @helper([[USIZE:i[0-9]+]])
94+
// CHECK: @helper([[USIZE:i[0-9]+]] %arg0)
9595
#[no_mangle]
9696
fn helper(_: usize) {
9797
}
9898

99-
// CHECK: @slice(i8* noalias nonnull readonly, [[USIZE]])
99+
// CHECK: @slice(i8* noalias nonnull readonly %arg0.ptr, [[USIZE]] %arg0.meta)
100100
// FIXME #25759 This should also have `nocapture`
101101
#[no_mangle]
102102
fn slice(_: &[u8]) {
103103
}
104104

105-
// CHECK: @mutable_slice(i8* nonnull, [[USIZE]])
105+
// CHECK: @mutable_slice(i8* nonnull %arg0.ptr, [[USIZE]] %arg0.meta)
106106
// FIXME #25759 This should also have `nocapture`
107107
// ... there's this LLVM bug that forces us to not use noalias, see #29485
108108
#[no_mangle]
109109
fn mutable_slice(_: &mut [u8]) {
110110
}
111111

112-
// CHECK: @unsafe_slice(%UnsafeInner* nonnull, [[USIZE]])
112+
// CHECK: @unsafe_slice(%UnsafeInner* nonnull %arg0.ptr, [[USIZE]] %arg0.meta)
113113
// unsafe interior means this isn't actually readonly and there may be aliases ...
114114
#[no_mangle]
115115
pub fn unsafe_slice(_: &[UnsafeInner]) {
116116
}
117117

118-
// CHECK: @str(i8* noalias nonnull readonly, [[USIZE]])
118+
// CHECK: @str(i8* noalias nonnull readonly %arg0.ptr, [[USIZE]] %arg0.meta)
119119
// FIXME #25759 This should also have `nocapture`
120120
#[no_mangle]
121121
fn str(_: &[u8]) {
@@ -132,7 +132,7 @@ fn trait_borrow(_: &Drop) {
132132
fn trait_box(_: Box<Drop>) {
133133
}
134134

135-
// CHECK: { i16*, [[USIZE]] } @return_slice(i16* noalias nonnull readonly, [[USIZE]])
135+
// CHECK: { i16*, [[USIZE]] } @return_slice(i16* noalias nonnull readonly %x.ptr, [[USIZE]] %x.meta)
136136
#[no_mangle]
137137
fn return_slice(x: &[u16]) -> &[u16] {
138138
x

src/test/codegen/move-val-init.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ pub struct Big {
2424
// CHECK-LABEL: @test_mvi
2525
#[no_mangle]
2626
pub unsafe fn test_mvi(target: *mut Big, make_big: fn() -> Big) {
27-
// CHECK: call void %1(%Big*{{[^%]*}} %0)
27+
// CHECK: call void %make_big(%Big*{{[^%]*}} %target)
2828
move_val_init(target, make_big());
2929
}

src/test/codegen/refs.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#![crate_type = "lib"]
1414

1515
// Hack to get the correct size for the length part in slices
16-
// CHECK: @helper([[USIZE:i[0-9]+]])
16+
// CHECK: @helper([[USIZE:i[0-9]+]] %arg0)
1717
#[no_mangle]
1818
fn helper(_: usize) {
1919
}
@@ -24,9 +24,9 @@ pub fn ref_dst(s: &[u8]) {
2424
// We used to generate an extra alloca and memcpy to ref the dst, so check that we copy
2525
// directly to the alloca for "x"
2626
// CHECK: [[X0:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %x, i32 0, i32 0
27-
// CHECK: store i8* %0, i8** [[X0]]
27+
// CHECK: store i8* %s.ptr, i8** [[X0]]
2828
// CHECK: [[X1:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %x, i32 0, i32 1
29-
// CHECK: store [[USIZE]] %1, [[USIZE]]* [[X1]]
29+
// CHECK: store [[USIZE]] %s.meta, [[USIZE]]* [[X1]]
3030

3131
let x = &*s;
3232
&x; // keep variable in an alloca

src/test/codegen/stores.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ pub struct Bytes {
2525
#[no_mangle]
2626
pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) {
2727
// CHECK: [[TMP:%.+]] = alloca i32
28-
// CHECK: %arg1 = alloca [4 x i8]
29-
// CHECK: store i32 %1, i32* [[TMP]]
30-
// CHECK: [[Y8:%[0-9]+]] = bitcast [4 x i8]* %arg1 to i8*
28+
// CHECK: %y = alloca [4 x i8]
29+
// CHECK: store i32 %0, i32* [[TMP]]
30+
// CHECK: [[Y8:%[0-9]+]] = bitcast [4 x i8]* %y to i8*
3131
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
3232
// CHECK: call void @llvm.memcpy.{{.*}}(i8* [[Y8]], i8* [[TMP8]], i{{[0-9]+}} 4, i32 1, i1 false)
3333
*x = y;
@@ -39,9 +39,9 @@ pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) {
3939
#[no_mangle]
4040
pub fn small_struct_alignment(x: &mut Bytes, y: Bytes) {
4141
// CHECK: [[TMP:%.+]] = alloca i32
42-
// CHECK: %arg1 = alloca %Bytes
43-
// CHECK: store i32 %1, i32* [[TMP]]
44-
// CHECK: [[Y8:%[0-9]+]] = bitcast %Bytes* %arg1 to i8*
42+
// CHECK: %y = alloca %Bytes
43+
// CHECK: store i32 %0, i32* [[TMP]]
44+
// CHECK: [[Y8:%[0-9]+]] = bitcast %Bytes* %y to i8*
4545
// CHECK: [[TMP8:%[0-9]+]] = bitcast i32* [[TMP]] to i8*
4646
// CHECK: call void @llvm.memcpy.{{.*}}(i8* [[Y8]], i8* [[TMP8]], i{{[0-9]+}} 4, i32 1, i1 false)
4747
*x = y;

0 commit comments

Comments
 (0)