Skip to content

Commit 4c15b6b

Browse files
authored
Rollup merge of #41361 - arielb1:move-mir-init, r=pnkfelix
lower `move_val_init` during MIR construction Because of its "magic" order-of-evaluation semantics, `move_val_init` must be lowered during MIR construction in order to work without needing a temporary. r? @eddyb
2 parents 33d4066 + ed3810b commit 4c15b6b

File tree

5 files changed

+79
-43
lines changed

5 files changed

+79
-43
lines changed

src/librustc_mir/build/expr/as_temp.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
3838
debug!("expr_as_temp(block={:?}, expr={:?})", block, expr);
3939
let this = self;
4040

41-
if let ExprKind::Scope { .. } = expr.kind {
42-
span_bug!(expr.span, "unexpected scope expression in as_temp: {:?}",
43-
expr);
41+
if let ExprKind::Scope { extent, value } = expr.kind {
42+
return this.in_scope(extent, block, |this| {
43+
this.as_temp(block, temp_lifetime, value)
44+
});
4445
}
4546

4647
let expr_ty = expr.ty.clone();

src/librustc_mir/build/expr/into.rs

+43-17
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use hair::*;
1616
use rustc::ty;
1717
use rustc::mir::*;
1818

19+
use syntax::abi::Abi;
20+
1921
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
2022
/// Compile `expr`, storing the result into `destination`, which
2123
/// is assumed to be uninitialized.
@@ -206,25 +208,49 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
206208
}
207209
_ => false
208210
};
211+
let intrinsic = match ty.sty {
212+
ty::TyFnDef(def_id, _, ref f) if
213+
f.abi() == Abi::RustIntrinsic ||
214+
f.abi() == Abi::PlatformIntrinsic =>
215+
{
216+
Some(this.hir.tcx().item_name(def_id).as_str())
217+
}
218+
_ => None
219+
};
220+
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
209221
let fun = unpack!(block = this.as_local_operand(block, fun));
210-
let args: Vec<_> =
211-
args.into_iter()
212-
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
213-
.collect();
222+
if intrinsic == Some("move_val_init") {
223+
// `move_val_init` has "magic" semantics - the second argument is
224+
// always evaluated "directly" into the first one.
214225

215-
let success = this.cfg.start_new_block();
216-
let cleanup = this.diverge_cleanup();
217-
this.cfg.terminate(block, source_info, TerminatorKind::Call {
218-
func: fun,
219-
args: args,
220-
cleanup: cleanup,
221-
destination: if diverges {
222-
None
223-
} else {
224-
Some ((destination.clone(), success))
225-
}
226-
});
227-
success.unit()
226+
let mut args = args.into_iter();
227+
let ptr = args.next().expect("0 arguments to `move_val_init`");
228+
let val = args.next().expect("1 argument to `move_val_init`");
229+
assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
230+
231+
let topmost_scope = this.topmost_scope();
232+
let ptr = unpack!(block = this.as_temp(block, Some(topmost_scope), ptr));
233+
this.into(&ptr.deref(), block, val)
234+
} else {
235+
let args: Vec<_> =
236+
args.into_iter()
237+
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
238+
.collect();
239+
240+
let success = this.cfg.start_new_block();
241+
let cleanup = this.diverge_cleanup();
242+
this.cfg.terminate(block, source_info, TerminatorKind::Call {
243+
func: fun,
244+
args: args,
245+
cleanup: cleanup,
246+
destination: if diverges {
247+
None
248+
} else {
249+
Some ((destination.clone(), success))
250+
}
251+
});
252+
success.unit()
253+
}
228254
}
229255

230256
// These cases don't actually need a destination

src/librustc_mir/shim.rs

+3-13
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
205205
patch: MirPatch::new(&mir),
206206
tcx, param_env
207207
};
208-
let dropee = Lvalue::Projection(
209-
box Projection {
210-
base: Lvalue::Local(Local::new(1+0)),
211-
elem: ProjectionElem::Deref
212-
}
213-
);
208+
let dropee = Lvalue::Local(Local::new(1+0)).deref();
214209
let resume_block = elaborator.patch.resume_block();
215210
elaborate_drops::elaborate_drop(
216211
&mut elaborator,
@@ -310,9 +305,7 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
310305

311306
let rcvr = match rcvr_adjustment {
312307
Adjustment::Identity => Operand::Consume(rcvr_l),
313-
Adjustment::Deref => Operand::Consume(Lvalue::Projection(
314-
box Projection { base: rcvr_l, elem: ProjectionElem::Deref }
315-
)),
308+
Adjustment::Deref => Operand::Consume(rcvr_l.deref()),
316309
Adjustment::RefMut => {
317310
// let rcvr = &mut rcvr;
318311
let re_erased = tcx.mk_region(ty::ReErased);
@@ -352,10 +345,7 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
352345
if let Some(untuple_args) = untuple_args {
353346
args.extend(untuple_args.iter().enumerate().map(|(i, ity)| {
354347
let arg_lv = Lvalue::Local(Local::new(1+1));
355-
Operand::Consume(Lvalue::Projection(box Projection {
356-
base: arg_lv,
357-
elem: ProjectionElem::Field(Field::new(i), *ity)
358-
}))
348+
Operand::Consume(arg_lv.field(Field::new(i), *ity))
359349
}));
360350
} else {
361351
args.extend((1..sig.inputs().len()).map(|i| {

src/librustc_trans/mir/block.rs

-10
Original file line numberDiff line numberDiff line change
@@ -418,16 +418,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
418418
};
419419
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
420420

421-
if intrinsic == Some("move_val_init") {
422-
let &(_, target) = destination.as_ref().unwrap();
423-
// The first argument is a thin destination pointer.
424-
let llptr = self.trans_operand(&bcx, &args[0]).immediate();
425-
let val = self.trans_operand(&bcx, &args[1]);
426-
self.store_operand(&bcx, llptr, None, val);
427-
funclet_br(self, bcx, target);
428-
return;
429-
}
430-
431421
if intrinsic == Some("transmute") {
432422
let &(ref dest, target) = destination.as_ref().unwrap();
433423
self.trans_transmute(&bcx, &args[0], dest);

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

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -C no-prepopulate-passes
12+
13+
#![feature(core_intrinsics)]
14+
#![crate_type = "lib"]
15+
16+
// test that `move_val_init` actually avoids big allocas
17+
18+
use std::intrinsics::move_val_init;
19+
20+
pub struct Big {
21+
pub data: [u8; 65536]
22+
}
23+
24+
// CHECK-LABEL: @test_mvi
25+
#[no_mangle]
26+
pub unsafe fn test_mvi(target: *mut Big, make_big: fn() -> Big) {
27+
// CHECK: call void %1(%Big*{{[^%]*}} %0)
28+
move_val_init(target, make_big());
29+
}

0 commit comments

Comments
 (0)