diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 70c41e7402d..3bf711d3e23 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1078,6 +1078,30 @@ impl<'a, 'tcx> CachedLayout { packed }; + // Unpack newtype ABIs. + if sized && optimize && size.bytes() > 0 { + // All but one field must be ZSTs, and so they all start at 0. + if offsets.iter().all(|o| o.bytes() == 0) { + let mut non_zst_fields = fields.iter().filter(|f| !f.is_zst()); + + // We have exactly one non-ZST field. + match (non_zst_fields.next(), non_zst_fields.next()) { + (Some(field), None) => { + // Field size match and it has a scalar ABI. + if size == field.size { + match field.abi { + Abi::Scalar(_) => { + abi = field.abi.clone(); + } + _ => {} + } + } + } + _ => {} + } + } + } + // Look for a scalar pair, as an ABI optimization. // FIXME(eddyb) ignore extra ZST fields and field ordering. if sized && !packed && fields.len() == 2 { @@ -1424,6 +1448,18 @@ impl<'a, 'tcx> CachedLayout { let mut st = univariant_uninterned(&variants[v], &def.repr, kind)?; st.variants = Variants::Single { index: v }; + // Exclude 0 from the range of a newtype ABI NonZero. + if Some(def.did) == cx.tcx().lang_items().non_zero() { + match st.abi { + Abi::Scalar(ref mut scalar) | + Abi::ScalarPair(ref mut scalar, _) => { + if scalar.valid_range.start == 0 { + scalar.valid_range.start = 1; + } + } + _ => {} + } + } return Ok(tcx.intern_layout(st)); } @@ -2284,20 +2320,6 @@ impl<'a, 'tcx> TyLayout<'tcx> { }; } - // Is this the NonZero lang item wrapping a pointer or integer type? - if let ty::TyAdt(def, _) = self.ty.sty { - if Some(def.did) == cx.tcx().lang_items().non_zero() { - let field = self.field(cx, 0)?; - let offset = self.fields.offset(0); - if let Abi::Scalar(Scalar { value, ref valid_range }) = field.abi { - return Ok(Some((offset, Scalar { - value, - valid_range: 0..=valid_range.end - }, 0))); - } - } - } - // Perhaps one of the fields is non-zero, let's recurse and find out. if let FieldPlacement::Union(_) = self.fields { // Only Rust enums have safe-to-inspect fields diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 3f3c5ac0a62..223379527c9 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -139,7 +139,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { let ccx = self.cx.ccx; if let mir::Lvalue::Projection(ref proj) = *lvalue { - // Allow uses of projections that are ZSTs or from immediate scalar fields. + // Allow uses of projections that are ZSTs or from scalar fields. if let LvalueContext::Consume = context { let base_ty = proj.base.ty(self.cx.mir, ccx.tcx()); let base_ty = self.cx.monomorphize(&base_ty); @@ -153,7 +153,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { if let mir::ProjectionElem::Field(..) = proj.elem { let layout = ccx.layout_of(base_ty.to_ty(ccx.tcx())); - if layout.is_llvm_scalar_pair() { + if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { // Recurse as a `Consume` instead of `Projection`, // potentially stopping at non-operand projections, // which would trigger `mark_as_lvalue` on locals. diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index e739037b07d..6811861499d 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -700,11 +700,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let elem = if field.is_zst() { C_undef(field.llvm_type(bcx.ccx)) } else { - bcx.extract_value(llval, tuple.layout.llvm_field_index(i)) + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + bcx.bitcast(llval, field.immediate_llvm_type(bcx.ccx)) }; // If the tuple is immediate, the elements are as well let op = OperandRef { - val: Immediate(base::to_immediate(bcx, elem, field)), + val: Immediate(elem), layout: field, }; self.trans_argument(bcx, op, llargs, &args[i]); @@ -712,7 +713,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } Pair(a, b) => { let elems = [a, b]; - for i in 0..tuple.layout.fields.count() { + assert_eq!(tuple.layout.fields.count(), 2); + for i in 0..2 { // Pair is always made up of immediates let op = OperandRef { val: Immediate(elems[i]), diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 318e36dc71a..f223227cd72 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -118,10 +118,27 @@ impl<'a, 'tcx> Const<'tcx> { fn get_field(&self, ccx: &CrateContext<'a, 'tcx>, i: usize) -> ValueRef { let layout = ccx.layout_of(self.ty); - if let layout::Abi::ScalarPair(..) = layout.abi { - const_get_elt(self.llval, i as u64) - } else { - const_get_elt(self.llval, layout.llvm_field_index(i)) + let field = layout.field(ccx, i); + if field.is_zst() { + return C_undef(field.immediate_llvm_type(ccx)); + } + match layout.abi { + layout::Abi::Scalar(_) => self.llval, + layout::Abi::ScalarPair(ref a, ref b) => { + let offset = layout.fields.offset(i); + if offset.bytes() == 0 { + assert_eq!(field.size, a.value.size(ccx)); + const_get_elt(self.llval, 0) + } else { + assert_eq!(offset, a.value.size(ccx) + .abi_align(b.value.align(ccx))); + assert_eq!(field.size, b.value.size(ccx)); + const_get_elt(self.llval, 1) + } + } + _ => { + const_get_elt(self.llval, layout.llvm_field_index(i)) + } } } @@ -159,7 +176,8 @@ impl<'a, 'tcx> Const<'tcx> { // a constant LLVM global and cast its address if necessary. let align = ccx.align_of(self.ty); let ptr = consts::addr_of(ccx, self.llval, align, "const"); - OperandValue::Ref(consts::ptrcast(ptr, llty.ptr_to()), Alignment::AbiAligned) + OperandValue::Ref(consts::ptrcast(ptr, layout.llvm_type(ccx).ptr_to()), + Alignment::AbiAligned) }; OperandRef { @@ -1179,12 +1197,26 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, -> Const<'tcx> { assert_eq!(vals.len(), layout.fields.count()); - if let layout::Abi::ScalarPair(..) = layout.abi { - assert_eq!(vals.len(), 2); - return Const::new(C_struct(ccx, &[ - vals[0].llval, - vals[1].llval, - ], false), layout.ty); + match layout.abi { + layout::Abi::Scalar(_) | + layout::Abi::ScalarPair(..) if discr.is_none() => { + let mut non_zst_fields = vals.iter().enumerate().map(|(i, f)| { + (f, layout.fields.offset(i)) + }).filter(|&(f, _)| !ccx.layout_of(f.ty).is_zst()); + match (non_zst_fields.next(), non_zst_fields.next()) { + (Some((x, offset)), None) if offset.bytes() == 0 => { + return Const::new(x.llval, layout.ty); + } + (Some((a, a_offset)), Some((b, _))) if a_offset.bytes() == 0 => { + return Const::new(C_struct(ccx, &[a.llval, b.llval], false), layout.ty); + } + (Some((a, _)), Some((b, b_offset))) if b_offset.bytes() == 0 => { + return Const::new(C_struct(ccx, &[b.llval, a.llval], false), layout.ty); + } + _ => {} + } + } + _ => {} } // offset of current value diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 97e7dda31aa..7826d998df3 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -10,12 +10,12 @@ use llvm::ValueRef; use rustc::ty; -use rustc::ty::layout::{LayoutOf, TyLayout}; +use rustc::ty::layout::{self, LayoutOf, TyLayout}; use rustc::mir; use rustc_data_structures::indexed_vec::Idx; use base; -use common::{CrateContext, C_undef}; +use common::{CrateContext, C_undef, C_usize}; use builder::Builder; use value::Value; use type_of::LayoutLlvmExt; @@ -207,24 +207,47 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { if let mir::ProjectionElem::Field(ref f, _) = proj.elem { if let Some(o) = self.maybe_trans_consume_direct(bcx, &proj.base) { let layout = o.layout.field(bcx.ccx, f.index()); + let offset = o.layout.fields.offset(f.index()); // Handled in `trans_consume`. assert!(!layout.is_zst()); - match o.val { - OperandValue::Pair(a, b) => { - let llval = [a, b][f.index()]; - // HACK(eddyb) have to bitcast pointers - // until LLVM removes pointee types. - let llval = bcx.bitcast(llval, - layout.immediate_llvm_type(bcx.ccx)); - return Some(OperandRef { - val: OperandValue::Immediate(llval), - layout - }); + // Offset has to match a scalar component. + let llval = match (o.val, &o.layout.abi) { + (OperandValue::Immediate(llval), + &layout::Abi::Scalar(ref scalar)) => { + assert_eq!(offset.bytes(), 0); + assert_eq!(layout.size, scalar.value.size(bcx.ccx)); + llval } - _ => {} - } + (OperandValue::Pair(a_llval, b_llval), + &layout::Abi::ScalarPair(ref a, ref b)) => { + if offset.bytes() == 0 { + assert_eq!(layout.size, a.value.size(bcx.ccx)); + a_llval + } else { + assert_eq!(offset, a.value.size(bcx.ccx) + .abi_align(b.value.align(bcx.ccx))); + assert_eq!(layout.size, b.value.size(bcx.ccx)); + b_llval + } + } + + // `#[repr(simd)]` types are also immediate. + (OperandValue::Immediate(llval), + &layout::Abi::Vector) => { + bcx.extract_element(llval, C_usize(bcx.ccx, f.index() as u64)) + } + + _ => return None + }; + + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + let llval = bcx.bitcast(llval, layout.immediate_llvm_type(bcx.ccx)); + return Some(OperandRef { + val: OperandValue::Immediate(llval), + layout + }); } } } diff --git a/src/test/codegen/function-arguments.rs b/src/test/codegen/function-arguments.rs index 428cbdddb22..f96c104b265 100644 --- a/src/test/codegen/function-arguments.rs +++ b/src/test/codegen/function-arguments.rs @@ -46,13 +46,13 @@ pub fn static_borrow(_: &'static i32) { pub fn named_borrow<'r>(_: &'r i32) { } -// CHECK: @unsafe_borrow(%UnsafeInner* dereferenceable(2) %arg0) +// CHECK: @unsafe_borrow(i16* dereferenceable(2) %arg0) // unsafe interior means this isn't actually readonly and there may be aliases ... #[no_mangle] pub fn unsafe_borrow(_: &UnsafeInner) { } -// CHECK: @mutable_unsafe_borrow(%UnsafeInner* dereferenceable(2) %arg0) +// CHECK: @mutable_unsafe_borrow(i16* dereferenceable(2) %arg0) // ... unless this is a mutable borrow, those never alias // ... except that there's this LLVM bug that forces us to not use noalias, see #29485 #[no_mangle] @@ -110,7 +110,7 @@ pub fn slice(_: &[u8]) { pub fn mutable_slice(_: &mut [u8]) { } -// CHECK: @unsafe_slice([0 x %UnsafeInner]* nonnull %arg0.0, [[USIZE]] %arg0.1) +// CHECK: @unsafe_slice([0 x i16]* nonnull %arg0.0, [[USIZE]] %arg0.1) // unsafe interior means this isn't actually readonly and there may be aliases ... #[no_mangle] pub fn unsafe_slice(_: &[UnsafeInner]) { diff --git a/src/test/codegen/issue-32031.rs b/src/test/codegen/issue-32031.rs index 5d3ccbfa4ce..e5ec1738545 100644 --- a/src/test/codegen/issue-32031.rs +++ b/src/test/codegen/issue-32031.rs @@ -15,7 +15,7 @@ #[no_mangle] pub struct F32(f32); -// CHECK: define float @add_newtype_f32(float, float) +// CHECK: define float @add_newtype_f32(float %a, float %b) #[inline(never)] #[no_mangle] pub fn add_newtype_f32(a: F32, b: F32) -> F32 { @@ -25,7 +25,7 @@ pub fn add_newtype_f32(a: F32, b: F32) -> F32 { #[no_mangle] pub struct F64(f64); -// CHECK: define double @add_newtype_f64(double, double) +// CHECK: define double @add_newtype_f64(double %a, double %b) #[inline(never)] #[no_mangle] pub fn add_newtype_f64(a: F64, b: F64) -> F64 {