From 3e473b1aaa4adf205c4d825a35fcd7b74dad1518 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 18 Apr 2017 15:05:04 +0300 Subject: [PATCH 1/2] use Lvalue helper functions in rustc_mir::shim --- src/librustc_mir/shim.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 0cec84d16a81c..4d70540a7c688 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -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| { From ed3810bf5e284b243b4500951652839235dd2113 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 18 Apr 2017 15:05:27 +0300 Subject: [PATCH 2/2] 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. --- src/librustc_mir/build/expr/as_temp.rs | 7 +-- src/librustc_mir/build/expr/into.rs | 60 ++++++++++++++++++-------- src/librustc_trans/mir/block.rs | 10 ----- src/test/codegen/move-val-init.rs | 29 +++++++++++++ 4 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 src/test/codegen/move-val-init.rs diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index e4598b4143871..a334923546fb2 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -38,9 +38,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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(); diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index a5a114c61bcf6..5982d3bdc81a4 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -16,6 +16,8 @@ use hair::*; 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } _ => false }; + 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()) + } + _ => None + }; + let intrinsic = intrinsic.as_ref().map(|s| &s[..]); 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(); + if intrinsic == Some("move_val_init") { + // `move_val_init` has "magic" semantics - the second argument is + // always evaluated "directly" into the first one. - 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() + 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 diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 0976859e27f44..0f5a38ac7f6b8 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -418,16 +418,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; 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); diff --git a/src/test/codegen/move-val-init.rs b/src/test/codegen/move-val-init.rs new file mode 100644 index 0000000000000..98b7db60b68fc --- /dev/null +++ b/src/test/codegen/move-val-init.rs @@ -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 or the MIT license +// , 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()); +}