about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMasaki Hara <ackie.h.gmai@gmail.com>2018-05-29 00:12:55 +0900
committerMasaki Hara <ackie.h.gmai@gmail.com>2018-08-19 08:07:33 +0900
commit800f2c13a3f4213648f301dcd4e10d80b1e6ea38 (patch)
treebb789909096a22e3d46f36033eda4d4edd897e2f /src
parente2b95cb70e2142aab82a40115d11ff54a975335e (diff)
downloadrust-800f2c13a3f4213648f301dcd4e10d80b1e6ea38.tar.gz
rust-800f2c13a3f4213648f301dcd4e10d80b1e6ea38.zip
Implement simple codegen for unsized rvalues.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_codegen_llvm/abi.rs29
-rw-r--r--src/librustc_codegen_llvm/base.rs2
-rw-r--r--src/librustc_codegen_llvm/mir/block.rs27
-rw-r--r--src/librustc_codegen_llvm/mir/mod.rs58
-rw-r--r--src/librustc_codegen_llvm/mir/operand.rs46
-rw-r--r--src/librustc_codegen_llvm/mir/place.rs21
-rw-r--r--src/librustc_codegen_llvm/mir/rvalue.rs26
-rw-r--r--src/librustc_codegen_llvm/mir/statement.rs7
-rw-r--r--src/librustc_target/abi/call/mod.rs28
-rw-r--r--src/librustc_target/abi/call/x86.rs1
-rw-r--r--src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs65
-rw-r--r--src/test/run-pass/unsized-locals/reference-unsized-locals.rs17
-rw-r--r--src/test/run-pass/unsized-locals/simple-unsized-locals.rs16
-rw-r--r--src/test/run-pass/unsized-locals/unsized-exprs.rs45
-rw-r--r--src/test/run-pass/unsized-locals/unsized-parameters.rs20
15 files changed, 388 insertions, 20 deletions
diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs
index 44982eee86b..b8a67a60e62 100644
--- a/src/librustc_codegen_llvm/abi.rs
+++ b/src/librustc_codegen_llvm/abi.rs
@@ -189,6 +189,8 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
         let cx = bx.cx;
         if self.is_indirect() {
             OperandValue::Ref(val, self.layout.align).store(bx, dst)
+        } else if self.is_unsized_indirect() {
+            bug!("unsized ArgType must be handled through store_fn_arg");
         } else if let PassMode::Cast(cast) = self.mode {
             // FIXME(eddyb): Figure out when the simpler Store is safe, clang
             // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
@@ -246,6 +248,9 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
             PassMode::Pair(..) => {
                 OperandValue::Pair(next(), next()).store(bx, dst);
             }
+            PassMode::UnsizedIndirect(..) => {
+                OperandValue::UnsizedRef(next(), next()).store(bx, dst);
+            }
             PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => {
                 self.store(bx, next(), dst);
             }
@@ -302,6 +307,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
             // Don't pass the vtable, it's not an argument of the virtual fn.
             // Instead, pass just the (thin pointer) first field of `*dyn Trait`.
             if arg_idx == Some(0) {
+                if layout.is_unsized() {
+                    unimplemented!("by-value trait object is not \
+                                    yet implemented in #![feature(unsized_locals)]");
+                }
                 // FIXME(eddyb) `layout.field(cx, 0)` is not enough because e.g.
                 // `Box<dyn Trait>` has a few newtype wrappers around the raw
                 // pointer, so we'd have to "dig down" to find `*dyn Trait`.
@@ -538,7 +547,9 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                 }
 
                 let size = arg.layout.size;
-                if size > layout::Pointer.size(cx) {
+                if arg.layout.is_unsized() {
+                    arg.make_unsized_indirect(None);
+                } else if size > layout::Pointer.size(cx) {
                     arg.make_indirect();
                 } else {
                     // We want to pass small aggregates as immediates, but using
@@ -584,6 +595,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                 llargument_tys.push(self.ret.memory_ty(cx).ptr_to());
                 Type::void(cx)
             }
+            PassMode::UnsizedIndirect(..) => bug!("return type must be sized"),
         };
 
         for arg in &self.args {
@@ -600,6 +612,13 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                     llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
                     continue;
                 }
+                PassMode::UnsizedIndirect(..) => {
+                    let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty);
+                    let ptr_layout = cx.layout_of(ptr_ty);
+                    llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
+                    llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
+                    continue;
+                }
                 PassMode::Cast(cast) => cast.llvm_type(cx),
                 PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(),
             };
@@ -651,6 +670,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                 PassMode::Ignore => {}
                 PassMode::Direct(ref attrs) |
                 PassMode::Indirect(ref attrs) => apply(attrs),
+                PassMode::UnsizedIndirect(ref attrs, ref extra_attrs) => {
+                    apply(attrs);
+                    apply(extra_attrs);
+                }
                 PassMode::Pair(ref a, ref b) => {
                     apply(a);
                     apply(b);
@@ -695,6 +718,10 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                 PassMode::Ignore => {}
                 PassMode::Direct(ref attrs) |
                 PassMode::Indirect(ref attrs) => apply(attrs),
+                PassMode::UnsizedIndirect(ref attrs, ref extra_attrs) => {
+                    apply(attrs);
+                    apply(extra_attrs);
+                }
                 PassMode::Pair(ref a, ref b) => {
                     apply(a);
                     apply(b);
diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs
index bd0c62e4766..49db35f5865 100644
--- a/src/librustc_codegen_llvm/base.rs
+++ b/src/librustc_codegen_llvm/base.rs
@@ -295,7 +295,7 @@ pub fn coerce_unsized_into(
             OperandValue::Immediate(base) => {
                 unsize_thin_ptr(bx, base, src_ty, dst_ty)
             }
-            OperandValue::Ref(..) => bug!()
+            OperandValue::Ref(..) | OperandValue::UnsizedRef(..) => bug!()
         };
         OperandValue::Pair(base, info).store(bx, dst);
     };
diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs
index 4e389c3b915..52f8576d0d1 100644
--- a/src/librustc_codegen_llvm/mir/block.rs
+++ b/src/librustc_codegen_llvm/mir/block.rs
@@ -32,7 +32,7 @@ use syntax_pos::Pos;
 use super::{FunctionCx, LocalRef};
 use super::place::PlaceRef;
 use super::operand::OperandRef;
-use super::operand::OperandValue::{Pair, Ref, Immediate};
+use super::operand::OperandValue::{Pair, Ref, UnsizedRef, Immediate};
 
 impl FunctionCx<'a, 'll, 'tcx> {
     pub fn codegen_block(&mut self, bb: mir::BasicBlock) {
@@ -234,6 +234,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
                         let op = self.codegen_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE));
                         if let Ref(llval, align) = op.val {
                             bx.load(llval, align)
+                        } else if let UnsizedRef(..) = op.val {
+                            bug!("return type must be sized");
                         } else {
                             op.immediate_or_packed_pair(&bx)
                         }
@@ -249,6 +251,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
                                     layout: cg_place.layout
                                 }
                             }
+                            LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
                         };
                         let llslot = match op.val {
                             Immediate(_) | Pair(..) => {
@@ -261,11 +264,14 @@ impl FunctionCx<'a, 'll, 'tcx> {
                                            "return place is unaligned!");
                                 llval
                             }
+                            UnsizedRef(..) => bug!("return type must be sized"),
                         };
                         bx.load(
                             bx.pointercast(llslot, cast_ty.llvm_type(bx.cx).ptr_to()),
                             self.fn_ty.ret.layout.align)
                     }
+
+                    PassMode::UnsizedIndirect(..) => bug!("return value must be sized"),
                 };
                 bx.ret(llval);
             }
@@ -607,6 +613,10 @@ impl FunctionCx<'a, 'll, 'tcx> {
                             op.val.store(&bx, tmp);
                             op.val = Ref(tmp.llval, tmp.align);
                         }
+                        (&mir::Operand::Copy(_), UnsizedRef(..)) |
+                        (&mir::Operand::Constant(_), UnsizedRef(..)) => {
+                            bug!("tried to pass an unsized argument by copy or constant")
+                        }
                         _ => {}
                     }
 
@@ -657,6 +667,15 @@ impl FunctionCx<'a, 'll, 'tcx> {
                 }
                 _ => bug!("codegen_argument: {:?} invalid for pair arugment", op)
             }
+        } else if let PassMode::UnsizedIndirect(..) = arg.mode {
+            match op.val {
+                UnsizedRef(a, b) => {
+                    llargs.push(a);
+                    llargs.push(b);
+                    return;
+                }
+                _ => bug!("codegen_argument: {:?} invalid for unsized indirect argument", op)
+            }
         }
 
         // Force by-ref if we have to load through a cast pointer.
@@ -686,6 +705,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
                     (llval, align, true)
                 }
             }
+            UnsizedRef(..) =>
+                bug!("codegen_argument: tried to pass unsized operand to sized argument"),
         };
 
         if by_ref && !arg.is_indirect() {
@@ -727,6 +748,8 @@ impl FunctionCx<'a, 'll, 'tcx> {
                 let field_ptr = tuple_ptr.project_field(bx, i);
                 self.codegen_argument(bx, field_ptr.load(bx), llargs, &args[i]);
             }
+        } else if let UnsizedRef(..) = tuple.val {
+            bug!("closure arguments must be sized")
         } else {
             // If the tuple is immediate, the elements are as well.
             for i in 0..tuple.layout.fields.count() {
@@ -820,6 +843,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
         let dest = if let mir::Place::Local(index) = *dest {
             match self.locals[index] {
                 LocalRef::Place(dest) => dest,
+                LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
                 LocalRef::Operand(None) => {
                     // Handle temporary places, specifically Operand ones, as
                     // they don't have allocas
@@ -871,6 +895,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
         if let mir::Place::Local(index) = *dst {
             match self.locals[index] {
                 LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place),
+                LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
                 LocalRef::Operand(None) => {
                     let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst));
                     assert!(!dst_layout.ty.has_erasable_regions());
diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs
index 8bb049be305..d7bad05a66c 100644
--- a/src/librustc_codegen_llvm/mir/mod.rs
+++ b/src/librustc_codegen_llvm/mir/mod.rs
@@ -180,6 +180,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
 
 enum LocalRef<'ll, 'tcx> {
     Place(PlaceRef<'ll, 'tcx>),
+    /// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place).
+    /// `*p` is the fat pointer that references the actual unsized place.
+    /// Every time it is initialized, we have to reallocate the place
+    /// and update the fat pointer. That's the reason why it is indirect.
+    UnsizedPlace(PlaceRef<'ll, 'tcx>),
     Operand(Option<OperandRef<'ll, 'tcx>>),
 }
 
@@ -275,17 +280,24 @@ pub fn codegen_mir(
                 }
 
                 debug!("alloc: {:?} ({}) -> place", local, name);
-                let place = PlaceRef::alloca(&bx, layout, &name.as_str());
-                if dbg {
-                    let (scope, span) = fx.debug_loc(mir::SourceInfo {
-                        span: decl.source_info.span,
-                        scope: decl.visibility_scope,
-                    });
-                    declare_local(&bx, &fx.debug_context, name, layout.ty, scope.unwrap(),
-                        VariableAccess::DirectVariable { alloca: place.llval },
-                        VariableKind::LocalVariable, span);
+                if layout.is_unsized() {
+                    let indirect_place =
+                        PlaceRef::alloca_unsized_indirect(&bx, layout, &name.as_str());
+                    // FIXME: add an appropriate debuginfo
+                    LocalRef::UnsizedPlace(indirect_place)
+                } else {
+                    let place = PlaceRef::alloca(&bx, layout, &name.as_str());
+                    if dbg {
+                        let (scope, span) = fx.debug_loc(mir::SourceInfo {
+                            span: decl.source_info.span,
+                            scope: decl.visibility_scope,
+                        });
+                        declare_local(&bx, &fx.debug_context, name, layout.ty, scope.unwrap(),
+                            VariableAccess::DirectVariable { alloca: place.llval },
+                            VariableKind::LocalVariable, span);
+                    }
+                    LocalRef::Place(place)
                 }
-                LocalRef::Place(place)
             } else {
                 // Temporary or return place
                 if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() {
@@ -294,7 +306,13 @@ pub fn codegen_mir(
                     LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align))
                 } else if memory_locals.contains(local) {
                     debug!("alloc: {:?} -> place", local);
-                    LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local)))
+                    if layout.is_unsized() {
+                        let indirect_place =
+                            PlaceRef::alloca_unsized_indirect(&bx, layout, &format!("{:?}", local));
+                        LocalRef::UnsizedPlace(indirect_place)
+                    } else {
+                        LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local)))
+                    }
                 } else {
                     // If this is an immediate local, we do not create an
                     // alloca in advance. Instead we wait until we see the
@@ -531,6 +549,18 @@ fn arg_local_refs(
             bx.set_value_name(llarg, &name);
             llarg_idx += 1;
             PlaceRef::new_sized(llarg, arg.layout, arg.layout.align)
+        } else if arg.is_unsized_indirect() {
+            // As the storage for the indirect argument lives during
+            // the whole function call, we just copy the fat pointer.
+            let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint);
+            llarg_idx += 1;
+            let llextra = llvm::get_param(bx.llfn(), llarg_idx as c_uint);
+            llarg_idx += 1;
+            let indirect_operand = OperandValue::Pair(llarg, llextra);
+
+            let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout, &name);
+            indirect_operand.store(&bx, tmp);
+            tmp
         } else {
             let tmp = PlaceRef::alloca(bx, arg.layout, &name);
             arg.store_fn_arg(bx, &mut llarg_idx, tmp);
@@ -632,7 +662,11 @@ fn arg_local_refs(
                 );
             }
         });
-        LocalRef::Place(place)
+        if arg.is_unsized_indirect() {
+            LocalRef::UnsizedPlace(place)
+        } else {
+            LocalRef::Place(place)
+        }
     }).collect()
 }
 
diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs
index f8166ee6491..ae929681b55 100644
--- a/src/librustc_codegen_llvm/mir/operand.rs
+++ b/src/librustc_codegen_llvm/mir/operand.rs
@@ -21,6 +21,8 @@ use common::{CodegenCx, C_undef, C_usize};
 use builder::{Builder, MemFlags};
 use value::Value;
 use type_of::LayoutLlvmExt;
+use type_::Type;
+use glue;
 
 use std::fmt;
 
@@ -36,6 +38,10 @@ pub enum OperandValue<'ll> {
     /// A reference to the actual operand. The data is guaranteed
     /// to be valid for the operand's lifetime.
     Ref(&'ll Value, Align),
+    /// A reference to the unsized operand. The data is guaranteed
+    /// to be valid for the operand's lifetime.
+    /// The second field is the extra.
+    UnsizedRef(&'ll Value, &'ll Value),
     /// A single LLVM value.
     Immediate(&'ll Value),
     /// A pair of immediate LLVM values. Used by fat pointers too.
@@ -148,7 +154,8 @@ impl OperandRef<'ll, 'tcx> {
         let (llptr, llextra) = match self.val {
             OperandValue::Immediate(llptr) => (llptr, None),
             OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
-            OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self)
+            OperandValue::Ref(..) |
+            OperandValue::UnsizedRef(..) => bug!("Deref of by-Ref operand {:?}", self)
         };
         let layout = cx.layout_of(projected_ty);
         PlaceRef {
@@ -243,7 +250,8 @@ impl OperandRef<'ll, 'tcx> {
                 *a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0, true));
                 *b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1, true));
             }
-            OperandValue::Ref(..) => bug!()
+            OperandValue::Ref(..) |
+            OperandValue::UnsizedRef(..) => bug!()
         }
 
         OperandRef {
@@ -287,6 +295,9 @@ impl OperandValue<'ll> {
                 base::memcpy_ty(bx, dest.llval, r, dest.layout,
                                 source_align.min(dest.align), flags)
             }
+            OperandValue::UnsizedRef(..) => {
+                bug!("cannot directly store unsized values");
+            }
             OperandValue::Immediate(s) => {
                 let val = base::from_immediate(bx, s);
                 bx.store_with_flags(val, dest.llval, dest.align, flags);
@@ -300,6 +311,35 @@ impl OperandValue<'ll> {
             }
         }
     }
+
+    pub fn store_unsized(self, bx: &Builder<'a, 'll, 'tcx>, indirect_dest: PlaceRef<'ll, 'tcx>) {
+        debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest);
+        let flags = MemFlags::empty();
+
+        // `indirect_dest` must have `*mut T` type. We extract `T` out of it.
+        let unsized_ty = indirect_dest.layout.ty.builtin_deref(true)
+            .unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest)).ty;
+
+        let (llptr, llextra) =
+            if let OperandValue::UnsizedRef(llptr, llextra) = self {
+                (llptr, llextra)
+            } else {
+                bug!("store_unsized called with a sized value")
+            };
+
+        // FIXME: choose an appropriate alignment, or use dynamic align somehow
+        let max_align = Align::from_bits(128, 128).unwrap();
+        let min_align = Align::from_bits(8, 8).unwrap();
+
+        // Allocate an appropriate region on the stack, and copy the value into it
+        let (llsize, _) = glue::size_and_align_of_dst(&bx, unsized_ty, Some(llextra));
+        let lldst = bx.array_alloca(Type::i8(bx.cx), llsize, "unsized_tmp", max_align);
+        base::call_memcpy(&bx, lldst, llptr, llsize, min_align, flags);
+
+        // Store the allocated region and the extra to the indirect place.
+        let indirect_operand = OperandValue::Pair(lldst, llextra);
+        indirect_operand.store(&bx, indirect_dest);
+    }
 }
 
 impl FunctionCx<'a, 'll, 'tcx> {
@@ -320,7 +360,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
                 LocalRef::Operand(None) => {
                     bug!("use of {:?} before def", place);
                 }
-                LocalRef::Place(..) => {
+                LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
                     // use path below
                 }
             }
diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs
index 6fa0845dd0c..6e5d3dad252 100644
--- a/src/librustc_codegen_llvm/mir/place.rs
+++ b/src/librustc_codegen_llvm/mir/place.rs
@@ -48,6 +48,7 @@ impl PlaceRef<'ll, 'tcx> {
         layout: TyLayout<'tcx>,
         align: Align,
     ) -> PlaceRef<'ll, 'tcx> {
+        assert!(!layout.is_unsized());
         PlaceRef {
             llval,
             llextra: None,
@@ -77,10 +78,21 @@ impl PlaceRef<'ll, 'tcx> {
     pub fn alloca(bx: &Builder<'a, 'll, 'tcx>, layout: TyLayout<'tcx>, name: &str)
                   -> PlaceRef<'ll, 'tcx> {
         debug!("alloca({:?}: {:?})", name, layout);
+        assert!(!layout.is_unsized(), "tried to statically allocate unsized place");
         let tmp = bx.alloca(layout.llvm_type(bx.cx), name, layout.align);
         Self::new_sized(tmp, layout, layout.align)
     }
 
+    /// Returns a place for an indirect reference to an unsized place.
+    pub fn alloca_unsized_indirect(bx: &Builder<'a, 'll, 'tcx>, layout: TyLayout<'tcx>, name: &str)
+                  -> PlaceRef<'ll, 'tcx> {
+        debug!("alloca_unsized_indirect({:?}: {:?})", name, layout);
+        assert!(layout.is_unsized(), "tried to allocate indirect place for sized values");
+        let ptr_ty = bx.cx.tcx.mk_mut_ptr(layout.ty);
+        let ptr_layout = bx.cx.layout_of(ptr_ty);
+        Self::alloca(bx, ptr_layout, name)
+    }
+
     pub fn len(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Value {
         if let layout::FieldPlacement::Array { count, .. } = self.layout.fields {
             if self.layout.is_unsized() {
@@ -97,7 +109,7 @@ impl PlaceRef<'ll, 'tcx> {
     pub fn load(&self, bx: &Builder<'a, 'll, 'tcx>) -> OperandRef<'ll, 'tcx> {
         debug!("PlaceRef::load: {:?}", self);
 
-        assert_eq!(self.llextra, None);
+        assert_eq!(self.llextra.is_some(), self.layout.is_unsized());
 
         if self.layout.is_zst() {
             return OperandRef::new_zst(bx.cx, self.layout);
@@ -119,7 +131,9 @@ impl PlaceRef<'ll, 'tcx> {
             }
         };
 
-        let val = if self.layout.is_llvm_immediate() {
+        let val = if let Some(llextra) = self.llextra {
+            OperandValue::UnsizedRef(self.llval, llextra)
+        } else if self.layout.is_llvm_immediate() {
             let mut const_llval = None;
             unsafe {
                 if let Some(global) = llvm::LLVMIsAGlobalVariable(self.llval) {
@@ -424,6 +438,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
                 LocalRef::Place(place) => {
                     return place;
                 }
+                LocalRef::UnsizedPlace(place) => {
+                    return place.load(bx).deref(&cx);
+                }
                 LocalRef::Operand(..) => {
                     bug!("using operand local {:?} as place", place);
                 }
diff --git a/src/librustc_codegen_llvm/mir/rvalue.rs b/src/librustc_codegen_llvm/mir/rvalue.rs
index 7eac8e735ed..ae318cda202 100644
--- a/src/librustc_codegen_llvm/mir/rvalue.rs
+++ b/src/librustc_codegen_llvm/mir/rvalue.rs
@@ -87,6 +87,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
                         let source = PlaceRef::new_sized(llref, operand.layout, align);
                         base::coerce_unsized_into(&bx, source, dest);
                     }
+                    OperandValue::UnsizedRef(..) => {
+                        bug!("unsized coercion on an unsized rvalue")
+                    }
                 }
                 bx
             }
@@ -175,6 +178,26 @@ impl FunctionCx<'a, 'll, 'tcx> {
         }
     }
 
+    pub fn codegen_rvalue_unsized(&mut self,
+                        bx: Builder<'a, 'll, 'tcx>,
+                        indirect_dest: PlaceRef<'ll, 'tcx>,
+                        rvalue: &mir::Rvalue<'tcx>)
+                        -> Builder<'a, 'll, 'tcx>
+    {
+        debug!("codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})",
+               indirect_dest.llval, rvalue);
+
+        match *rvalue {
+            mir::Rvalue::Use(ref operand) => {
+                let cg_operand = self.codegen_operand(&bx, operand);
+                cg_operand.val.store_unsized(&bx, indirect_dest);
+                bx
+            }
+
+            _ => bug!("unsized assignment other than Rvalue::Use"),
+        }
+    }
+
     pub fn codegen_rvalue_operand(&mut self,
                                 bx: Builder<'a, 'll, 'tcx>,
                                 rvalue: &mir::Rvalue<'tcx>)
@@ -245,6 +268,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
                                 bug!("by-ref operand {:?} in codegen_rvalue_operand",
                                      operand);
                             }
+                            OperandValue::UnsizedRef(..) => {
+                                bug!("unsized coercion on an unsized rvalue")
+                            }
                         }
                     }
                     mir::CastKind::Misc if operand.layout.is_llvm_scalar_pair() => {
diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs
index 06340a3e5d8..dd62a12553c 100644
--- a/src/librustc_codegen_llvm/mir/statement.rs
+++ b/src/librustc_codegen_llvm/mir/statement.rs
@@ -31,6 +31,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
                         LocalRef::Place(cg_dest) => {
                             self.codegen_rvalue(bx, cg_dest, rvalue)
                         }
+                        LocalRef::UnsizedPlace(cg_indirect_dest) => {
+                            self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
+                        }
                         LocalRef::Operand(None) => {
                             let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
                             self.locals[index] = LocalRef::Operand(Some(operand));
@@ -61,12 +64,16 @@ impl FunctionCx<'a, 'll, 'tcx> {
             mir::StatementKind::StorageLive(local) => {
                 if let LocalRef::Place(cg_place) = self.locals[local] {
                     cg_place.storage_live(&bx);
+                } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
+                    cg_indirect_place.storage_live(&bx);
                 }
                 bx
             }
             mir::StatementKind::StorageDead(local) => {
                 if let LocalRef::Place(cg_place) = self.locals[local] {
                     cg_place.storage_dead(&bx);
+                } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
+                    cg_indirect_place.storage_dead(&bx);
                 }
                 bx
             }
diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs
index af874b1035b..8647beef3d5 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -45,6 +45,8 @@ pub enum PassMode {
     Cast(CastTarget),
     /// Pass the argument indirectly via a hidden pointer.
     Indirect(ArgAttributes),
+    /// Pass the unsized argument indirectly via a hidden pointer.
+    UnsizedIndirect(ArgAttributes, ArgAttributes),
 }
 
 // Hack to disable non_upper_case_globals only for the bitflags! and not for the rest
@@ -381,6 +383,25 @@ impl<'a, Ty> ArgType<'a, Ty> {
         }
     }
 
+    pub fn make_unsized_indirect(&mut self, vtable_size: Option<Size>) {
+        self.make_indirect();
+
+        let attrs = if let PassMode::Indirect(attrs) = self.mode {
+            attrs
+        } else {
+            unreachable!()
+        };
+
+        let mut extra_attrs = ArgAttributes::new();
+        if let Some(vtable_size) = vtable_size {
+            extra_attrs.set(ArgAttribute::NoAlias)
+                       .set(ArgAttribute::NonNull);
+            extra_attrs.pointee_size = vtable_size;
+        }
+
+        self.mode = PassMode::UnsizedIndirect(attrs, extra_attrs);
+    }
+
     pub fn extend_integer_width_to(&mut self, bits: u64) {
         // Only integers have signedness
         if let Abi::Scalar(ref scalar) = self.layout.abi {
@@ -414,6 +435,13 @@ impl<'a, Ty> ArgType<'a, Ty> {
         }
     }
 
+    pub fn is_unsized_indirect(&self) -> bool {
+        match self.mode {
+            PassMode::UnsizedIndirect(..) => true,
+            _ => false
+        }
+    }
+
     pub fn is_ignore(&self) -> bool {
         self.mode == PassMode::Ignore
     }
diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs
index 0c0040de9df..6e64210a890 100644
--- a/src/librustc_target/abi/call/x86.rs
+++ b/src/librustc_target/abi/call/x86.rs
@@ -102,6 +102,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: C, fty: &mut FnType<'a, Ty>, flavor: Flav
                 PassMode::Indirect(_) => continue,
                 PassMode::Direct(ref mut attrs) => attrs,
                 PassMode::Pair(..) |
+                PassMode::UnsizedIndirect(..) |
                 PassMode::Cast(_) => {
                     unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
                 }
diff --git a/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs b/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs
new file mode 100644
index 00000000000..e1fda427b4e
--- /dev/null
+++ b/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs
@@ -0,0 +1,65 @@
+// Copyright 2018 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.
+
+#![feature(unsized_locals)]
+
+use std::fmt;
+
+fn gen_foo() -> Box<fmt::Display> {
+    Box::new(Box::new("foo"))
+}
+
+fn foo(x: fmt::Display) {
+    assert_eq!(x.to_string(), "foo");
+}
+
+fn foo_indirect(x: fmt::Display) {
+    foo(x);
+}
+
+fn main() {
+    foo(*gen_foo());
+    foo_indirect(*gen_foo());
+
+    {
+        let x: fmt::Display = *gen_foo();
+        foo(x);
+    }
+
+    {
+        let x: fmt::Display = *gen_foo();
+        let y: fmt::Display = *gen_foo();
+        foo(x);
+        foo(y);
+    }
+
+    {
+        let mut cnt: usize = 3;
+        let x = loop {
+            let x: fmt::Display = *gen_foo();
+            if cnt == 0 {
+                break x;
+            } else {
+                cnt -= 1;
+            }
+        };
+        foo(x);
+    }
+
+    {
+        let x: fmt::Display = *gen_foo();
+        let x = if true {
+            x
+        } else {
+            *gen_foo()
+        };
+        foo(x);
+    }
+}
diff --git a/src/test/run-pass/unsized-locals/reference-unsized-locals.rs b/src/test/run-pass/unsized-locals/reference-unsized-locals.rs
new file mode 100644
index 00000000000..6ed39a78648
--- /dev/null
+++ b/src/test/run-pass/unsized-locals/reference-unsized-locals.rs
@@ -0,0 +1,17 @@
+// Copyright 2018 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.
+
+#![feature(unsized_locals)]
+
+fn main() {
+    let foo: Box<[u8]> = Box::new(*b"foo");
+    let foo: [u8] = *foo;
+    assert_eq!(&foo, b"foo" as &[u8]);
+}
diff --git a/src/test/run-pass/unsized-locals/simple-unsized-locals.rs b/src/test/run-pass/unsized-locals/simple-unsized-locals.rs
new file mode 100644
index 00000000000..0b1aa6225eb
--- /dev/null
+++ b/src/test/run-pass/unsized-locals/simple-unsized-locals.rs
@@ -0,0 +1,16 @@
+// Copyright 2018 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.
+
+#![feature(unsized_locals)]
+
+fn main() {
+    let foo: Box<[u8]> = Box::new(*b"foo");
+    let _foo: [u8] = *foo;
+}
diff --git a/src/test/run-pass/unsized-locals/unsized-exprs.rs b/src/test/run-pass/unsized-locals/unsized-exprs.rs
new file mode 100644
index 00000000000..9a5e534db25
--- /dev/null
+++ b/src/test/run-pass/unsized-locals/unsized-exprs.rs
@@ -0,0 +1,45 @@
+// Copyright 2018 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.
+
+#![feature(unsized_tuple_coercion, unsized_locals)]
+
+struct A<X: ?Sized>(X);
+
+fn udrop<T: ?Sized>(_x: T) {}
+fn foo() -> Box<[u8]> {
+    Box::new(*b"foo")
+}
+fn tfoo() -> Box<(i32, [u8])> {
+    Box::new((42, *b"foo"))
+}
+fn afoo() -> Box<A<[u8]>> {
+    Box::new(A(*b"foo"))
+}
+
+impl std::ops::Add<i32> for A<[u8]> {
+    type Output = ();
+    fn add(self, _rhs: i32) -> Self::Output {}
+}
+
+fn main() {
+    udrop::<[u8]>(loop {
+        break *foo();
+    });
+    udrop::<[u8]>(if true {
+        *foo()
+    } else {
+        *foo()
+    });
+    udrop::<[u8]>({*foo()});
+    #[allow(unused_parens)]
+    udrop::<[u8]>((*foo()));
+    udrop::<[u8]>((*tfoo()).1);
+    *afoo() + 42;
+}
diff --git a/src/test/run-pass/unsized-locals/unsized-parameters.rs b/src/test/run-pass/unsized-locals/unsized-parameters.rs
new file mode 100644
index 00000000000..0314fe1d686
--- /dev/null
+++ b/src/test/run-pass/unsized-locals/unsized-parameters.rs
@@ -0,0 +1,20 @@
+// Copyright 2018 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.
+
+#![feature(unsized_locals)]
+
+pub fn f0(_f: dyn FnOnce()) {}
+pub fn f1(_s: str) {}
+pub fn f2((_x, _y): (i32, [i32])) {}
+
+fn main() {
+    let foo = "foo".to_string().into_boxed_str();
+    f1(*foo);
+}