about summary refs log tree commit diff
path: root/compiler/rustc_codegen_llvm
diff options
context:
space:
mode:
authorgnzlbg <gonzalobg88@gmail.com>2019-07-13 17:16:57 +0200
committerAshley Mannix <kodraus@hey.com>2020-11-08 12:01:48 +1000
commit6e88e96ccf1ca7621e6177d729a69625838db1c8 (patch)
tree2c40921ebb7710ac9d5de81fb6ccccfb8e47169c /compiler/rustc_codegen_llvm
parent9d78d1d02761b906038ba4d54c5f3427f920f5fb (diff)
downloadrust-6e88e96ccf1ca7621e6177d729a69625838db1c8.tar.gz
rust-6e88e96ccf1ca7621e6177d729a69625838db1c8.zip
Support repr(simd) on ADTs containing a single array field
This PR allows using `#[repr(simd)]` on ADTs containing a
single array field:

```rust
 #[repr(simd)] struct S0([f32; 4]);
 #[repr(simd)] struct S1<const N: usize>([f32; N]);
 #[repr(simd)] struct S2<T, const N: usize>([T; N]);
```

This should allow experimenting with portable packed SIMD
abstractions on nightly that make use of const generics.
Diffstat (limited to 'compiler/rustc_codegen_llvm')
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs122
1 files changed, 72 insertions, 50 deletions
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index e9900e8bc10..2ea7d7ac2d8 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -740,6 +740,23 @@ fn generic_simd_intrinsic(
     llret_ty: &'ll Type,
     span: Span,
 ) -> Result<&'ll Value, ()> {
+    // Given a SIMD vector type `x` return the element type and the number of
+    // elements in the vector.
+    fn simd_ty_and_len(bx: &Builder<'a, 'll, 'tcx>, simd_ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
+        let ty = if let ty::Adt(_def, _substs) = simd_ty.kind() {
+            let f0_ty = bx.layout_of(simd_ty).field(bx, 0).ty;
+            if let ty::Array(element_ty, _) = f0_ty.kind() { element_ty } else { f0_ty }
+        } else {
+            bug!("should only be called with a SIMD type")
+        };
+        let count = if let abi::Abi::Vector { count, .. } = bx.layout_of(simd_ty).abi {
+            count
+        } else {
+            bug!("should only be called with a SIMD type")
+        };
+        (ty, count)
+    }
+
     // macros for error handling:
     macro_rules! emit_error {
         ($msg: tt) => {
@@ -792,7 +809,7 @@ fn generic_simd_intrinsic(
             _ => return_error!("`{}` is not an integral type", in_ty),
         };
         require_simd!(arg_tys[1], "argument");
-        let v_len = arg_tys[1].simd_size(tcx);
+        let (_, v_len) = simd_ty_and_len(bx, arg_tys[1]);
         require!(
             // Allow masks for vectors with fewer than 8 elements to be
             // represented with a u8 or i8.
@@ -812,8 +829,6 @@ fn generic_simd_intrinsic(
     // every intrinsic below takes a SIMD vector as its first argument
     require_simd!(arg_tys[0], "input");
     let in_ty = arg_tys[0];
-    let in_elem = arg_tys[0].simd_type(tcx);
-    let in_len = arg_tys[0].simd_size(tcx);
 
     let comparison = match name {
         sym::simd_eq => Some(hir::BinOpKind::Eq),
@@ -825,14 +840,15 @@ fn generic_simd_intrinsic(
         _ => None,
     };
 
+    let (in_elem, in_len) = simd_ty_and_len(bx, arg_tys[0]);
     if let Some(cmp_op) = comparison {
         require_simd!(ret_ty, "return");
 
-        let out_len = ret_ty.simd_size(tcx);
+        let (out_ty, out_len) = simd_ty_and_len(bx, ret_ty);
         require!(
             in_len == out_len,
             "expected return type with length {} (same as input type `{}`), \
-                  found `{}` with length {}",
+             found `{}` with length {}",
             in_len,
             in_ty,
             ret_ty,
@@ -842,7 +858,7 @@ fn generic_simd_intrinsic(
             bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
             "expected return type with integer elements, found `{}` with non-integer `{}`",
             ret_ty,
-            ret_ty.simd_type(tcx)
+            out_ty
         );
 
         return Ok(compare_simd_types(
@@ -862,7 +878,7 @@ fn generic_simd_intrinsic(
 
         require_simd!(ret_ty, "return");
 
-        let out_len = ret_ty.simd_size(tcx);
+        let (out_ty, out_len) = simd_ty_and_len(bx, ret_ty);
         require!(
             out_len == n,
             "expected return type of length {}, found `{}` with length {}",
@@ -871,13 +887,13 @@ fn generic_simd_intrinsic(
             out_len
         );
         require!(
-            in_elem == ret_ty.simd_type(tcx),
+            in_elem == out_ty,
             "expected return element type `{}` (element of input `{}`), \
-                  found `{}` with element type `{}`",
+             found `{}` with element type `{}`",
             in_elem,
             in_ty,
             ret_ty,
-            ret_ty.simd_type(tcx)
+            out_ty
         );
 
         let total_len = u128::from(in_len) * 2;
@@ -946,7 +962,7 @@ fn generic_simd_intrinsic(
         let m_elem_ty = in_elem;
         let m_len = in_len;
         require_simd!(arg_tys[1], "argument");
-        let v_len = arg_tys[1].simd_size(tcx);
+        let (_, v_len) = simd_ty_and_len(bx, arg_tys[1]);
         require!(
             m_len == v_len,
             "mismatched lengths: mask length `{}` != other vector length `{}`",
@@ -1171,25 +1187,27 @@ fn generic_simd_intrinsic(
         require_simd!(ret_ty, "return");
 
         // Of the same length:
+        let (_, out_len) = simd_ty_and_len(bx, arg_tys[1]);
+        let (_, out_len2) = simd_ty_and_len(bx, arg_tys[2]);
         require!(
-            in_len == arg_tys[1].simd_size(tcx),
+            in_len == out_len,
             "expected {} argument with length {} (same as input type `{}`), \
-                  found `{}` with length {}",
+             found `{}` with length {}",
             "second",
             in_len,
             in_ty,
             arg_tys[1],
-            arg_tys[1].simd_size(tcx)
+            out_len
         );
         require!(
-            in_len == arg_tys[2].simd_size(tcx),
+            in_len == out_len2,
             "expected {} argument with length {} (same as input type `{}`), \
-                  found `{}` with length {}",
+             found `{}` with length {}",
             "third",
             in_len,
             in_ty,
             arg_tys[2],
-            arg_tys[2].simd_size(tcx)
+            out_len2
         );
 
         // The return type must match the first argument type
@@ -1213,39 +1231,40 @@ fn generic_simd_intrinsic(
 
         // The second argument must be a simd vector with an element type that's a pointer
         // to the element type of the first argument
-        let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() {
-            ty::RawPtr(p) if p.ty == in_elem => {
-                (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx)))
-            }
+        let (element_ty0, _) = simd_ty_and_len(bx, arg_tys[0]);
+        let (element_ty1, _) = simd_ty_and_len(bx, arg_tys[1]);
+        let (pointer_count, underlying_ty) = match element_ty1.kind() {
+            ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)),
             _ => {
                 require!(
                     false,
                     "expected element type `{}` of second argument `{}` \
-                                 to be a pointer to the element type `{}` of the first \
-                                 argument `{}`, found `{}` != `*_ {}`",
-                    arg_tys[1].simd_type(tcx),
+                        to be a pointer to the element type `{}` of the first \
+                        argument `{}`, found `{}` != `*_ {}`",
+                    element_ty1,
                     arg_tys[1],
                     in_elem,
                     in_ty,
-                    arg_tys[1].simd_type(tcx),
+                    element_ty1,
                     in_elem
                 );
                 unreachable!();
             }
         };
         assert!(pointer_count > 0);
-        assert_eq!(pointer_count - 1, ptr_count(arg_tys[0].simd_type(tcx)));
-        assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx)));
+        assert_eq!(pointer_count - 1, ptr_count(element_ty0));
+        assert_eq!(underlying_ty, non_ptr(element_ty0));
 
         // The element type of the third argument must be a signed integer type of any width:
-        match arg_tys[2].simd_type(tcx).kind() {
+        let (element_ty2, _) = simd_ty_and_len(bx, arg_tys[2]);
+        match element_ty2.kind() {
             ty::Int(_) => (),
             _ => {
                 require!(
                     false,
                     "expected element type `{}` of third argument `{}` \
                                  to be a signed integer type",
-                    arg_tys[2].simd_type(tcx),
+                    element_ty2,
                     arg_tys[2]
                 );
             }
@@ -1297,25 +1316,27 @@ fn generic_simd_intrinsic(
         require_simd!(arg_tys[2], "third");
 
         // Of the same length:
+        let (_, element_len1) = simd_ty_and_len(bx, arg_tys[1]);
+        let (_, element_len2) = simd_ty_and_len(bx, arg_tys[2]);
         require!(
-            in_len == arg_tys[1].simd_size(tcx),
+            in_len == element_len1,
             "expected {} argument with length {} (same as input type `{}`), \
-                  found `{}` with length {}",
+            found `{}` with length {}",
             "second",
             in_len,
             in_ty,
             arg_tys[1],
-            arg_tys[1].simd_size(tcx)
+            element_len1
         );
         require!(
-            in_len == arg_tys[2].simd_size(tcx),
+            in_len == element_len2,
             "expected {} argument with length {} (same as input type `{}`), \
-                  found `{}` with length {}",
+            found `{}` with length {}",
             "third",
             in_len,
             in_ty,
             arg_tys[2],
-            arg_tys[2].simd_size(tcx)
+            element_len2
         );
 
         // This counts how many pointers
@@ -1336,39 +1357,42 @@ fn generic_simd_intrinsic(
 
         // The second argument must be a simd vector with an element type that's a pointer
         // to the element type of the first argument
-        let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() {
+        let (element_ty0, _element_len0) = simd_ty_and_len(bx, arg_tys[0]);
+        let (element_ty1, _element_len1) = simd_ty_and_len(bx, arg_tys[1]);
+        let (element_ty2, _element_len2) = simd_ty_and_len(bx, arg_tys[2]);
+        let (pointer_count, underlying_ty) = match element_ty1.kind() {
             ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => {
-                (ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx)))
+                (ptr_count(element_ty1), non_ptr(element_ty1))
             }
             _ => {
                 require!(
                     false,
                     "expected element type `{}` of second argument `{}` \
-                                 to be a pointer to the element type `{}` of the first \
-                                 argument `{}`, found `{}` != `*mut {}`",
-                    arg_tys[1].simd_type(tcx),
+                        to be a pointer to the element type `{}` of the first \
+                        argument `{}`, found `{}` != `*mut {}`",
+                    element_ty1,
                     arg_tys[1],
                     in_elem,
                     in_ty,
-                    arg_tys[1].simd_type(tcx),
+                    element_ty1,
                     in_elem
                 );
                 unreachable!();
             }
         };
         assert!(pointer_count > 0);
-        assert_eq!(pointer_count - 1, ptr_count(arg_tys[0].simd_type(tcx)));
-        assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx)));
+        assert_eq!(pointer_count - 1, ptr_count(element_ty0));
+        assert_eq!(underlying_ty, non_ptr(element_ty0));
 
         // The element type of the third argument must be a signed integer type of any width:
-        match arg_tys[2].simd_type(tcx).kind() {
+        match element_ty2.kind() {
             ty::Int(_) => (),
             _ => {
                 require!(
                     false,
                     "expected element type `{}` of third argument `{}` \
-                                 to be a signed integer type",
-                    arg_tys[2].simd_type(tcx),
+                         be a signed integer type",
+                    element_ty2,
                     arg_tys[2]
                 );
             }
@@ -1565,7 +1589,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
 
     if name == sym::simd_cast {
         require_simd!(ret_ty, "return");
-        let out_len = ret_ty.simd_size(tcx);
+        let (out_elem, out_len) = simd_ty_and_len(bx, ret_ty);
         require!(
             in_len == out_len,
             "expected return type with length {} (same as input type `{}`), \
@@ -1576,8 +1600,6 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
             out_len
         );
         // casting cares about nominal type, not just structural type
-        let out_elem = ret_ty.simd_type(tcx);
-
         if in_elem == out_elem {
             return Ok(args[0].immediate());
         }
@@ -1693,7 +1715,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
                 return_error!(
                     "expected element type `{}` of vector type `{}` \
                      to be a signed or unsigned integer type",
-                    arg_tys[0].simd_type(tcx),
+                    simd_ty_and_len(bx, arg_tys[0]).0,
                     arg_tys[0]
                 );
             }