about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-10-09 02:31:06 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-11-19 02:43:55 +0200
commit37a7521ef93b2e2d7a4cd04df38929d841b8ffcc (patch)
tree30287acb7166ed268a6a3e3f911f5d536cacc1c0
parent0b8697241f3988bfa55b9f17d60732dab0e3d75d (diff)
downloadrust-37a7521ef93b2e2d7a4cd04df38929d841b8ffcc.tar.gz
rust-37a7521ef93b2e2d7a4cd04df38929d841b8ffcc.zip
rustc: unpack scalar newtype layout ABIs.
-rw-r--r--src/librustc/ty/layout.rs50
-rw-r--r--src/librustc_trans/mir/analyze.rs4
-rw-r--r--src/librustc_trans/mir/block.rs8
-rw-r--r--src/librustc_trans/mir/constant.rs54
-rw-r--r--src/librustc_trans/mir/operand.rs53
-rw-r--r--src/test/codegen/function-arguments.rs6
-rw-r--r--src/test/codegen/issue-32031.rs4
7 files changed, 129 insertions, 50 deletions
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<T>.
+                    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 {