From 800f2c13a3f4213648f301dcd4e10d80b1e6ea38 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Tue, 29 May 2018 00:12:55 +0900 Subject: [PATCH] Implement simple codegen for unsized rvalues. --- src/librustc_codegen_llvm/abi.rs | 29 ++++++++- src/librustc_codegen_llvm/base.rs | 2 +- src/librustc_codegen_llvm/mir/block.rs | 27 +++++++- src/librustc_codegen_llvm/mir/mod.rs | 58 +++++++++++++---- src/librustc_codegen_llvm/mir/operand.rs | 46 ++++++++++++- src/librustc_codegen_llvm/mir/place.rs | 21 +++++- src/librustc_codegen_llvm/mir/rvalue.rs | 26 ++++++++ src/librustc_codegen_llvm/mir/statement.rs | 7 ++ src/librustc_target/abi/call/mod.rs | 28 ++++++++ src/librustc_target/abi/call/x86.rs | 1 + .../long-live-the-unsized-temporary.rs | 65 +++++++++++++++++++ .../reference-unsized-locals.rs | 17 +++++ .../unsized-locals/simple-unsized-locals.rs | 16 +++++ .../run-pass/unsized-locals/unsized-exprs.rs | 45 +++++++++++++ .../unsized-locals/unsized-parameters.rs | 20 ++++++ 15 files changed, 388 insertions(+), 20 deletions(-) create mode 100644 src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs create mode 100644 src/test/run-pass/unsized-locals/reference-unsized-locals.rs create mode 100644 src/test/run-pass/unsized-locals/simple-unsized-locals.rs create mode 100644 src/test/run-pass/unsized-locals/unsized-exprs.rs create mode 100644 src/test/run-pass/unsized-locals/unsized-parameters.rs 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 @@ fn store(&self, bx: &Builder<'_, 'll, 'tcx>, val: &'ll Value, dst: PlaceRef<'ll, 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 @@ fn store_fn_arg(&self, bx: &Builder<'a, 'll, 'tcx>, idx: &mut usize, dst: PlaceR 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 @@ fn new_vtable(cx: &CodegenCx<'ll, '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` has a few newtype wrappers around the raw // pointer, so we'd have to "dig down" to find `*dyn Trait`. @@ -538,7 +547,9 @@ fn adjust_for_abi(&mut self, } 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 @@ fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type { 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 @@ fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type { 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 @@ fn apply_attrs_llfn(&self, llfn: &'ll Value) { 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 @@ fn apply_attrs_callsite(&self, bx: &Builder<'a, 'll, 'tcx>, callsite: &'ll Value 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 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 @@ fn codegen_terminator(&mut self, 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 @@ fn codegen_terminator(&mut self, layout: cg_place.layout } } + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), }; let llslot = match op.val { Immediate(_) | Pair(..) => { @@ -261,11 +264,14 @@ fn codegen_terminator(&mut self, "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 @@ fn codegen_terminator(&mut self, 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 @@ fn codegen_argument(&mut self, } _ => 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 @@ fn codegen_argument(&mut self, (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 @@ fn codegen_arguments_untupled(&mut self, 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 @@ fn make_return_dest(&mut self, bx: &Builder<'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 @@ fn codegen_transmute(&mut self, bx: &Builder<'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 @@ fn scope_metadata_for_loc(&self, scope_id: mir::SourceScope, pos: BytePos) 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>), } @@ -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 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 @@ pub fn deref(self, cx: &CodegenCx<'ll, 'tcx>) -> PlaceRef<'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 @@ pub fn extract_field(&self, bx: &Builder<'a, 'll, 'tcx>, i: usize) -> OperandRef *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 @@ fn store_with_flags( 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 @@ fn store_with_flags( } } } + + 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 @@ fn maybe_codegen_consume_direct(&mut self, 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 @@ pub fn new_sized( layout: TyLayout<'tcx>, align: Align, ) -> PlaceRef<'ll, 'tcx> { + assert!(!layout.is_unsized()); PlaceRef { llval, llextra: None, @@ -77,10 +78,21 @@ pub fn from_const_alloc( 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 @@ pub fn len(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Value { 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 @@ pub fn load(&self, bx: &Builder<'a, 'll, 'tcx>) -> OperandRef<'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 @@ pub fn codegen_place(&mut self, 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 @@ pub fn codegen_rvalue(&mut self, 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 @@ pub fn codegen_rvalue(&mut self, } } + 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 @@ pub fn codegen_rvalue_operand(&mut self, 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 @@ pub fn codegen_statement(&mut self, 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 @@ pub fn codegen_statement(&mut self, 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 @@ pub fn make_indirect_byval(&mut self) { } } + pub fn make_unsized_indirect(&mut self, vtable_size: Option) { + 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 @@ pub fn is_indirect(&self) -> bool { } } + 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 or the MIT license +// , 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 { + 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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); + +fn udrop(_x: T) {} +fn foo() -> Box<[u8]> { + Box::new(*b"foo") +} +fn tfoo() -> Box<(i32, [u8])> { + Box::new((42, *b"foo")) +} +fn afoo() -> Box> { + Box::new(A(*b"foo")) +} + +impl std::ops::Add 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 or the MIT license +// , 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); +} -- 2.44.0