Skip to content

Commit aabfed5

Browse files
committed
Auto merge of #45996 - eddyb:even-mirer-1, r=arielb1
MIR: hide .rodata constants vs by-ref ABI clash in trans. Back in #45380, constants were copied into locals during MIR creation to ensure that arguments ' memory can be used by the callee, if the constant is placed in `.rodata` and the ABI passes it by-ref. However, there are several drawbacks (see #45380 (comment)), most importantly the complication of constant propagation (UB if a constant ends up in `Call` arguments) and inconveniencing analyses. Instead, I've modified the `rustc_trans` implementation of calls to copy an `Operand::Constant` argument locally if it's not immediate, and added a test that segfaults without the copy. cc @dotdash @arielb1
2 parents 02eed2e + 6db6893 commit aabfed5

File tree

7 files changed

+36
-23
lines changed

7 files changed

+36
-23
lines changed

src/librustc/mir/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -684,9 +684,10 @@ pub enum TerminatorKind<'tcx> {
684684
Call {
685685
/// The function that’s being called
686686
func: Operand<'tcx>,
687-
/// Arguments the function is called with. These are owned by the callee, which is free to
688-
/// modify them. This is important as "by-value" arguments might be passed by-reference at
689-
/// the ABI level.
687+
/// Arguments the function is called with.
688+
/// These are owned by the callee, which is free to modify them.
689+
/// This allows the memory occupied by "by-value" arguments to be
690+
/// reused across function calls without duplicating the contents.
690691
args: Vec<Operand<'tcx>>,
691692
/// Destination for the return value. If some, the call is converging.
692693
destination: Option<(Lvalue<'tcx>, BasicBlock)>,

src/librustc_mir/build/expr/into.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
247247
} else {
248248
let args: Vec<_> =
249249
args.into_iter()
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-
})
250+
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
257251
.collect();
258252

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

src/librustc_trans/mir/block.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,16 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
524524
}
525525
}
526526

527-
let op = self.trans_operand(&bcx, arg);
527+
let mut op = self.trans_operand(&bcx, arg);
528+
529+
// The callee needs to own the argument memory if we pass it
530+
// by-ref, so make a local copy of non-immediate constants.
531+
if let (&mir::Operand::Constant(_), Ref(..)) = (arg, op.val) {
532+
let tmp = LvalueRef::alloca(&bcx, op.ty, "const");
533+
self.store_operand(&bcx, tmp.llval, tmp.alignment.to_align(), op);
534+
op.val = Ref(tmp.llval, tmp.alignment);
535+
}
536+
528537
self.trans_argument(&bcx, op, &mut llargs, &fn_ty,
529538
&mut idx, &mut llfn, &def);
530539
}

src/test/mir-opt/nll/liveness-call-subtlety.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,15 @@ fn main() {
3131
// | Live variables at bb0[0]: []
3232
// StorageLive(_1);
3333
// | Live variables at bb0[1]: []
34-
// StorageLive(_2);
35-
// | Live variables at bb0[2]: []
36-
// _2 = const 22usize;
37-
// | Live variables at bb0[3]: [_2]
38-
// _1 = const <std::boxed::Box<T>>::new(_2) -> bb1;
34+
// _1 = const <std::boxed::Box<T>>::new(const 22usize) -> bb1;
3935
// }
4036
// END rustc.main.nll.0.mir
4137
// START rustc.main.nll.0.mir
4238
// | Live variables on entry to bb1: [_1 (drop)]
4339
// bb1: {
4440
// | Live variables at bb1[0]: [_1 (drop)]
45-
// StorageDead(_2);
41+
// StorageLive(_2);
4642
// | Live variables at bb1[1]: [_1 (drop)]
47-
// StorageLive(_3);
48-
// | Live variables at bb1[2]: [_1 (drop)]
49-
// _3 = const can_panic() -> [return: bb2, unwind: bb4];
43+
// _2 = const can_panic() -> [return: bb2, unwind: bb4];
5044
// }
5145
// END rustc.main.nll.0.mir

src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,5 @@ impl<T> Drop for Wrap<T> {
4646

4747
// END RUST SOURCE
4848
// START rustc.main.nll.0.mir
49-
// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]}
49+
// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb8[0]}
5050
// END rustc.main.nll.0.mir

src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,5 @@ fn main() {
4545
// ...
4646
// _2 = &'_#1r _1[_3];
4747
// ...
48-
// _2 = &'_#3r (*_11);
48+
// _2 = &'_#3r (*_10);
4949
// END rustc.main.nll.0.mir

src/test/run-pass/mir_trans_calls.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(fn_traits)]
11+
#![feature(fn_traits, test)]
12+
13+
extern crate test;
1214

1315
fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) {
1416
// Test passing a number of arguments including a fat pointer.
@@ -156,6 +158,16 @@ fn test_fn_nested_pair(x: &((f32, f32), u32)) -> (f32, f32) {
156158
(z.0, z.1)
157159
}
158160

161+
fn test_fn_const_arg_by_ref(mut a: [u64; 4]) -> u64 {
162+
// Mutate the by-reference argument, which won't work with
163+
// a non-immediate constant unless it's copied to the stack.
164+
let a = test::black_box(&mut a);
165+
a[0] += a[1];
166+
a[0] += a[2];
167+
a[0] += a[3];
168+
a[0]
169+
}
170+
159171
fn main() {
160172
assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..]));
161173
assert_eq!(test2(98), 98);
@@ -182,4 +194,7 @@ fn main() {
182194
assert_eq!(test_fn_ignored_pair_0(), ());
183195
assert_eq!(test_fn_ignored_pair_named(), (Foo, Foo));
184196
assert_eq!(test_fn_nested_pair(&((1.0, 2.0), 0)), (1.0, 2.0));
197+
198+
const ARRAY: [u64; 4] = [1, 2, 3, 4];
199+
assert_eq!(test_fn_const_arg_by_ref(ARRAY), 1 + 2 + 3 + 4);
185200
}

0 commit comments

Comments
 (0)