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
This commit is contained in:
commit
f448cdff9d
@ -38,9 +38,10 @@ fn expr_as_temp(&mut self,
|
||||
debug!("expr_as_temp(block={:?}, expr={:?})", block, expr);
|
||||
let this = self;
|
||||
|
||||
if let ExprKind::Scope { .. } = expr.kind {
|
||||
span_bug!(expr.span, "unexpected scope expression in as_temp: {:?}",
|
||||
expr);
|
||||
if let ExprKind::Scope { extent, value } = expr.kind {
|
||||
return this.in_scope(extent, block, |this| {
|
||||
this.as_temp(block, temp_lifetime, value)
|
||||
});
|
||||
}
|
||||
|
||||
let expr_ty = expr.ty.clone();
|
||||
|
@ -16,6 +16,8 @@
|
||||
use rustc::ty;
|
||||
use rustc::mir::*;
|
||||
|
||||
use syntax::abi::Abi;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||
/// Compile `expr`, storing the result into `destination`, which
|
||||
/// is assumed to be uninitialized.
|
||||
@ -206,25 +208,49 @@ pub fn into_expr(&mut self,
|
||||
}
|
||||
_ => false
|
||||
};
|
||||
let fun = unpack!(block = this.as_local_operand(block, fun));
|
||||
let args: Vec<_> =
|
||||
args.into_iter()
|
||||
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
|
||||
.collect();
|
||||
|
||||
let success = this.cfg.start_new_block();
|
||||
let cleanup = this.diverge_cleanup();
|
||||
this.cfg.terminate(block, source_info, TerminatorKind::Call {
|
||||
func: fun,
|
||||
args: args,
|
||||
cleanup: cleanup,
|
||||
destination: if diverges {
|
||||
None
|
||||
} else {
|
||||
Some ((destination.clone(), success))
|
||||
let intrinsic = match ty.sty {
|
||||
ty::TyFnDef(def_id, _, ref f) if
|
||||
f.abi() == Abi::RustIntrinsic ||
|
||||
f.abi() == Abi::PlatformIntrinsic =>
|
||||
{
|
||||
Some(this.hir.tcx().item_name(def_id).as_str())
|
||||
}
|
||||
});
|
||||
success.unit()
|
||||
_ => None
|
||||
};
|
||||
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
|
||||
let fun = unpack!(block = this.as_local_operand(block, fun));
|
||||
if intrinsic == Some("move_val_init") {
|
||||
// `move_val_init` has "magic" semantics - the second argument is
|
||||
// always evaluated "directly" into the first one.
|
||||
|
||||
let mut args = args.into_iter();
|
||||
let ptr = args.next().expect("0 arguments to `move_val_init`");
|
||||
let val = args.next().expect("1 argument to `move_val_init`");
|
||||
assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
|
||||
|
||||
let topmost_scope = this.topmost_scope();
|
||||
let ptr = unpack!(block = this.as_temp(block, Some(topmost_scope), ptr));
|
||||
this.into(&ptr.deref(), block, val)
|
||||
} else {
|
||||
let args: Vec<_> =
|
||||
args.into_iter()
|
||||
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
|
||||
.collect();
|
||||
|
||||
let success = this.cfg.start_new_block();
|
||||
let cleanup = this.diverge_cleanup();
|
||||
this.cfg.terminate(block, source_info, TerminatorKind::Call {
|
||||
func: fun,
|
||||
args: args,
|
||||
cleanup: cleanup,
|
||||
destination: if diverges {
|
||||
None
|
||||
} else {
|
||||
Some ((destination.clone(), success))
|
||||
}
|
||||
});
|
||||
success.unit()
|
||||
}
|
||||
}
|
||||
|
||||
// These cases don't actually need a destination
|
||||
|
@ -205,12 +205,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||
patch: MirPatch::new(&mir),
|
||||
tcx, param_env
|
||||
};
|
||||
let dropee = Lvalue::Projection(
|
||||
box Projection {
|
||||
base: Lvalue::Local(Local::new(1+0)),
|
||||
elem: ProjectionElem::Deref
|
||||
}
|
||||
);
|
||||
let dropee = Lvalue::Local(Local::new(1+0)).deref();
|
||||
let resume_block = elaborator.patch.resume_block();
|
||||
elaborate_drops::elaborate_drop(
|
||||
&mut elaborator,
|
||||
@ -310,9 +305,7 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||
|
||||
let rcvr = match rcvr_adjustment {
|
||||
Adjustment::Identity => Operand::Consume(rcvr_l),
|
||||
Adjustment::Deref => Operand::Consume(Lvalue::Projection(
|
||||
box Projection { base: rcvr_l, elem: ProjectionElem::Deref }
|
||||
)),
|
||||
Adjustment::Deref => Operand::Consume(rcvr_l.deref()),
|
||||
Adjustment::RefMut => {
|
||||
// let rcvr = &mut rcvr;
|
||||
let re_erased = tcx.mk_region(ty::ReErased);
|
||||
@ -352,10 +345,7 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||
if let Some(untuple_args) = untuple_args {
|
||||
args.extend(untuple_args.iter().enumerate().map(|(i, ity)| {
|
||||
let arg_lv = Lvalue::Local(Local::new(1+1));
|
||||
Operand::Consume(Lvalue::Projection(box Projection {
|
||||
base: arg_lv,
|
||||
elem: ProjectionElem::Field(Field::new(i), *ity)
|
||||
}))
|
||||
Operand::Consume(arg_lv.field(Field::new(i), *ity))
|
||||
}));
|
||||
} else {
|
||||
args.extend((1..sig.inputs().len()).map(|i| {
|
||||
|
@ -418,16 +418,6 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock,
|
||||
};
|
||||
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
|
||||
|
||||
if intrinsic == Some("move_val_init") {
|
||||
let &(_, target) = destination.as_ref().unwrap();
|
||||
// The first argument is a thin destination pointer.
|
||||
let llptr = self.trans_operand(&bcx, &args[0]).immediate();
|
||||
let val = self.trans_operand(&bcx, &args[1]);
|
||||
self.store_operand(&bcx, llptr, None, val);
|
||||
funclet_br(self, bcx, target);
|
||||
return;
|
||||
}
|
||||
|
||||
if intrinsic == Some("transmute") {
|
||||
let &(ref dest, target) = destination.as_ref().unwrap();
|
||||
self.trans_transmute(&bcx, &args[0], dest);
|
||||
|
29
src/test/codegen/move-val-init.rs
Normal file
29
src/test/codegen/move-val-init.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![feature(core_intrinsics)]
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// test that `move_val_init` actually avoids big allocas
|
||||
|
||||
use std::intrinsics::move_val_init;
|
||||
|
||||
pub struct Big {
|
||||
pub data: [u8; 65536]
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @test_mvi
|
||||
#[no_mangle]
|
||||
pub unsafe fn test_mvi(target: *mut Big, make_big: fn() -> Big) {
|
||||
// CHECK: call void %1(%Big*{{[^%]*}} %0)
|
||||
move_val_init(target, make_big());
|
||||
}
|
Loading…
Reference in New Issue
Block a user