From 3e473b1aaa4adf205c4d825a35fcd7b74dad1518 Mon Sep 17 00:00:00 2001
From: Ariel Ben-Yehuda <ariel.byd@gmail.com>
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 <ariel.byd@gmail.com>
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 <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());
+}