From 257158fe0a1ca0e20734a444532950819359944e Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Sat, 13 Jul 2019 17:16:57 +0200 Subject: [PATCH 01/13] 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([f32; N]); #[repr(simd)] struct S2([T; N]); ``` This should allow experimenting with portable packed SIMD abstractions on nightly that make use of const generics. --- src/librustc/ty/layout.rs | 124 +++++++++++++--- src/librustc/ty/sty.rs | 16 --- src/librustc_codegen_llvm/intrinsic.rs | 132 +++++++++++------- src/librustc_typeck/check/mod.rs | 1 + .../simd-intrinsic-generic-extract-insert.rs | 47 +++++++ .../simd-intrinsic-transmute-array.rs | 18 +++ src/test/ui/simd/simd-array-type.rs | 44 ++++++ src/test/ui/simd/simd-generics.rs | 32 ++++- ...intrinsic-generic-arithmetic-saturating.rs | 68 ++++----- .../simd/simd-intrinsic-generic-arithmetic.rs | 63 +++++---- 10 files changed, 390 insertions(+), 155 deletions(-) create mode 100644 src/test/codegen/simd-intrinsic/simd-intrinsic-generic-extract-insert.rs create mode 100644 src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs create mode 100644 src/test/ui/simd/simd-array-type.rs diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 1c9a5ad621854..859b6bcc65a1d 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -687,34 +687,117 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => { - let element = self.layout_of(ty.simd_type(tcx))?; - let count = ty.simd_size(tcx) as u64; - assert!(count > 0); - let scalar = match element.abi { - Abi::Scalar(ref scalar) => scalar.clone(), - _ => { - tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \ - a non-machine element type `{}`", - ty, element.ty)); + ty::Adt(def, substs) if def.repr.simd() => { + // Supported SIMD vectors are homogeneous ADTs with at least one field: + // + // * #[repr(simd)] struct S(T, T, T, T); + // * #[repr(simd)] struct S { x: T, y: T, z: T, w: T } + // * #[repr(simd)] struct S([T; 4]) + // + // where T is a "machine type", e.g., `f32`, `i64`, `*mut _`. + + // SIMD vectors with zero fields are not supported: + if def.non_enum_variant().fields.is_empty() { + tcx.sess.fatal(&format!( + "monomorphising SIMD type `{}` of zero length", ty + )); + } + + // Type of the first ADT field: + let f0_ty = def.non_enum_variant().fields[0].ty(tcx, substs); + + // Heterogeneous SIMD vectors are not supported: + for fi in &def.non_enum_variant().fields { + if fi.ty(tcx, substs) != f0_ty { + tcx.sess.fatal(&format!( + "monomorphising heterogeneous SIMD type `{}`", ty + )); } + } + + // The element type and number of elements of the SIMD vector + // are obtained from: + // + // * the element type and length of the single array field, if + // the first field is of array type, or + // + // * the homogenous field type and the number of fields. + let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.sty { + // First ADT field is an array: + + // SIMD vectors with multiple array fields are not supported: + if def.non_enum_variant().fields.len() != 1 { + tcx.sess.fatal(&format!( + "monomorphising SIMD type `{}` with more than one array field", + ty + )); + } + + // Extract the number of elements from the layout of the array field: + let len = if let Ok(TyLayout{ + details: LayoutDetails { + fields: FieldPlacement::Array { + count, .. + }, .. + }, .. + }) = self.layout_of(f0_ty) { + count + } else { + unreachable!(); + }; + + (e_ty, *len, true) + } else { + // First ADT field is not an array: + (f0_ty, def.non_enum_variant().fields.len() as _, false) }; - let size = element.size.checked_mul(count, dl) + + // SIMD vectors of zero length are not supported: + if e_len == 0 { + tcx.sess.fatal(&format!( + "monomorphising SIMD type `{}` of zero length", ty + )); + } + + // Compute the ABI of the element type: + let e_ly = self.layout_of(e_ty)?; + let e_abi = if let Abi::Scalar(ref scalar) = e_ly.abi { + scalar.clone() + } else { + tcx.sess.fatal(&format!( + "monomorphising SIMD type `{}` with a non-machine element type `{}`", + ty, e_ty + )) + }; + + + // Compute the size and alignment of the vector: + let size = e_ly.size.checked_mul(e_len, dl) .ok_or(LayoutError::SizeOverflow(ty))?; let align = dl.vector_align(size); let size = size.align_to(align.abi); + // Compute the placement of the vector fields: + let fields = if is_array { + FieldPlacement::Arbitrary { + offsets: vec![Size::ZERO], + memory_index: vec![0], + } + } else { + FieldPlacement::Array { + stride: e_ly.size, + count: e_len, + } + }; + tcx.intern_layout(LayoutDetails { variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldPlacement::Array { - stride: element.size, - count - }, + fields, abi: Abi::Vector { - element: scalar, - count + element: e_abi, + count: e_len, }, - largest_niche: element.largest_niche.clone(), + largest_niche: e_ly.largest_niche.clone(), size, align, }) @@ -2170,11 +2253,6 @@ where ty::Tuple(tys) => tys[i].expect_ty(), - // SIMD vector types. - ty::Adt(def, ..) if def.repr.simd() => { - this.ty.simd_type(tcx) - } - // ADTs. ty::Adt(def, substs) => { match this.variants { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 064c374de2b4c..6bdc62744b6b9 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1823,22 +1823,6 @@ impl<'tcx> TyS<'tcx> { } } - pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.sty { - Adt(def, substs) => { - def.non_enum_variant().fields[0].ty(tcx, substs) - } - _ => bug!("simd_type called on invalid type") - } - } - - pub fn simd_size(&self, _cx: TyCtxt<'_>) -> usize { - match self.sty { - Adt(def, _) => def.non_enum_variant().fields.len(), - _ => bug!("simd_size called on invalid type") - } - } - #[inline] pub fn is_region_ptr(&self) -> bool { match self.sty { diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 199170182e4b4..65996c4980acb 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -1039,6 +1039,29 @@ 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_type_size(bx: &Builder<'a, 'll, 'tcx>, x: Ty<'tcx>) -> (Ty<'tcx>, usize) { + let ty = if let ty::Adt(def, substs) = x.sty { + assert!(def.repr.simd()); + let field0_ty = def.non_enum_variant().fields[0].ty(bx.tcx(), substs); + if let ty::Array(element_ty, _) = field0_ty.sty { + element_ty + } else { + field0_ty + } + } else { + bug!("should only be called with a SIMD type") + }; + let count = if let ty::layout::Abi::Vector{count, ..} = bx.layout_of(x).abi { + count + } else { + bug!("should only be called with a SIMD type") + }; + (ty, count as usize) + } + + // macros for error handling: macro_rules! emit_error { ($msg: tt) => { @@ -1076,8 +1099,9 @@ fn generic_simd_intrinsic( } let tcx = bx.tcx(); + let param_env = ty::ParamEnv::reveal_all(); let sig = tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), + param_env, &callee_ty.fn_sig(tcx), ); let arg_tys = sig.inputs(); @@ -1092,7 +1116,9 @@ 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_type_size(bx, arg_tys[1]); require!(m_len == v_len, "mismatched lengths: mask length `{}` != other vector length `{}`", m_len, v_len @@ -1106,8 +1132,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 { "simd_eq" => Some(hir::BinOpKind::Eq), @@ -1119,19 +1143,23 @@ fn generic_simd_intrinsic( _ => None }; + let (in_elem, in_len) = simd_type_size(bx, arg_tys[0]); if let Some(cmp_op) = comparison { require_simd!(ret_ty, "return"); - let out_len = ret_ty.simd_size(tcx); - require!(in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, in_ty, - ret_ty, out_len); - require!(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)); + let (out_ty, out_len) = simd_type_size(bx, ret_ty); + require!( + in_len == out_len, + "expected return type with length {} (same as input type `{}`), \ + found `{}` with length {}", + in_len, in_ty, + ret_ty, out_len + ); + require!( + bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, + "expected return type with integer elements, found `{}` with non-integer `{}`", + ret_ty, out_ty + ); return Ok(compare_simd_types(bx, args[0].immediate(), @@ -1147,15 +1175,15 @@ fn generic_simd_intrinsic( require_simd!(ret_ty, "return"); - let out_len = ret_ty.simd_size(tcx); + let (out_ty, out_len) = simd_type_size(bx, ret_ty); require!(out_len == n, "expected return type of length {}, found `{}` with length {}", n, ret_ty, out_len); - require!(in_elem == ret_ty.simd_type(tcx), + require!(in_elem == out_ty, "expected return element type `{}` (element of input `{}`), \ found `{}` with element type `{}`", in_elem, in_ty, - ret_ty, ret_ty.simd_type(tcx)); + ret_ty, out_ty); let total_len = in_len as u128 * 2; @@ -1208,7 +1236,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_type_size(bx, arg_tys[1]); require!(m_len == v_len, "mismatched lengths: mask length `{}` != other vector length `{}`", m_len, v_len @@ -1424,14 +1452,16 @@ fn generic_simd_intrinsic( require_simd!(ret_ty, "return"); // Of the same length: - require!(in_len == arg_tys[1].simd_size(tcx), + let (_, out_len) = simd_type_size(bx, arg_tys[1]); + let (_, out_len2) = simd_type_size(bx, arg_tys[2]); + require!(in_len == out_len, "expected {} argument with length {} (same as input type `{}`), \ found `{}` with length {}", "second", in_len, in_ty, arg_tys[1], - arg_tys[1].simd_size(tcx)); - require!(in_len == arg_tys[2].simd_size(tcx), + out_len); + require!(in_len == out_len2, "expected {} argument with length {} (same as input type `{}`), \ 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 require!(ret_ty == in_ty, @@ -1456,29 +1486,32 @@ 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).sty { - 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_type_size(bx, arg_tys[0]); + let (element_ty1, _) = simd_type_size(bx, arg_tys[1]); + let (pointer_count, underlying_ty) = match element_ty1.sty { + 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), arg_tys[1], in_elem, in_ty, - arg_tys[1].simd_type(tcx), in_elem); + element_ty1, arg_tys[1], in_elem, in_ty, + 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).sty { + let (element_ty2, _) = simd_type_size(bx, arg_tys[2]); + match element_ty2.sty { ty::Int(_) => (), _ => { require!(false, "expected element type `{}` of third argument `{}` \ to be a signed integer type", - arg_tys[2].simd_type(tcx), arg_tys[2]); + element_ty2, arg_tys[2]); } } @@ -1528,14 +1561,16 @@ fn generic_simd_intrinsic( require_simd!(arg_tys[2], "third"); // Of the same length: - require!(in_len == arg_tys[1].simd_size(tcx), + let (_, element_len1) = simd_type_size(bx, arg_tys[1]); + let (_, element_len2) = simd_type_size(bx, arg_tys[2]); + require!(in_len == element_len1, "expected {} argument with length {} (same as input type `{}`), \ found `{}` with length {}", "second", in_len, in_ty, arg_tys[1], - arg_tys[1].simd_size(tcx)); - require!(in_len == arg_tys[2].simd_size(tcx), + element_len1); + require!(in_len == element_len2, "expected {} argument with length {} (same as input type `{}`), \ found `{}` with length {}", "third", in_len, in_ty, arg_tys[2], - arg_tys[2].simd_size(tcx)); + element_len2); // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { @@ -1555,30 +1590,33 @@ 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).sty { + let (element_ty0, _element_len0) = simd_type_size(bx, arg_tys[0]); + let (element_ty1, _element_len1) = simd_type_size(bx, arg_tys[1]); + let (element_ty2, _element_len2) = simd_type_size(bx, arg_tys[2]); + let (pointer_count, underlying_ty) = match element_ty1.sty { ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::MutMutable - => (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), arg_tys[1], in_elem, in_ty, - arg_tys[1].simd_type(tcx), in_elem); + element_ty1, arg_tys[1], in_elem, in_ty, + 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).sty { + match element_ty2.sty { ty::Int(_) => (), _ => { require!(false, "expected element type `{}` of third argument `{}` \ to be a signed integer type", - arg_tys[2].simd_type(tcx), arg_tys[2]); + element_ty2, arg_tys[2]); } } @@ -1755,15 +1793,13 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, if name == "simd_cast" { require_simd!(ret_ty, "return"); - let out_len = ret_ty.simd_size(tcx); + let (out_elem, out_len) = simd_type_size(bx, ret_ty); require!(in_len == out_len, "expected return type with length {} (same as input type `{}`), \ found `{}` with length {}", in_len, in_ty, ret_ty, 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()); } enum Style { Float, Int(/* is signed? */ bool), Unsupported } @@ -1877,7 +1913,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), arg_tys[0] + simd_type_size(bx, arg_tys[0]).0, arg_tys[0] ); } }; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 08033b46b8004..a9ff1eb6c5fdf 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1761,6 +1761,7 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { match e.sty { ty::Param(_) => { /* struct(T, T, T, T) is ok */ } _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ } + ty::Array(ty, _c) if ty.is_machine() => { /* struct([f32; 4]) */ } _ => { span_err!(tcx.sess, sp, E0077, "SIMD vector element type should be machine type"); diff --git a/src/test/codegen/simd-intrinsic/simd-intrinsic-generic-extract-insert.rs b/src/test/codegen/simd-intrinsic/simd-intrinsic-generic-extract-insert.rs new file mode 100644 index 0000000000000..3dba044d376e8 --- /dev/null +++ b/src/test/codegen/simd-intrinsic/simd-intrinsic-generic-extract-insert.rs @@ -0,0 +1,47 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] + +#![feature(repr_simd, platform_intrinsics, const_generics)] +#![allow(non_camel_case_types, incomplete_features)] + +#[repr(simd)] +#[derive(Copy, Clone)] +pub struct M(pub f32, pub f32, pub f32, pub f32); + +#[repr(simd)] +#[derive(Copy, Clone)] +pub struct S([f32; N]); + +extern "platform-intrinsic" { + fn simd_extract(x: T, idx: u32) -> U; + fn simd_insert(x: T, idx: u32, b: U) -> T; +} + +// CHECK-LABEL: @extract_m +#[no_mangle] +pub unsafe fn extract_m(v: M, i: u32) -> f32 { + // CHECK: extractelement <4 x float> %0, i32 %i + simd_extract(v, i) +} + +// CHECK-LABEL: @extract_s +#[no_mangle] +pub unsafe fn extract_s(v: S<4>, i: u32) -> f32 { + // CHECK: extractelement <4 x float> %0, i32 %i + simd_extract(v, i) +} + +// CHECK-LABEL: @insert_m +#[no_mangle] +pub unsafe fn insert_m(v: M, i: u32, j: f32) -> M { + // CHECK: insertelement <4 x float> %1, float %j, i32 %i + simd_insert(v, i, j) +} + +// CHECK-LABEL: @insert_s +#[no_mangle] +pub unsafe fn insert_s(v: S<4>, i: u32, j: f32) -> S<4> { + // CHECK: insertelement <4 x float> %1, float %j, i32 %i + simd_insert(v, i, j) +} diff --git a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs new file mode 100644 index 0000000000000..4520ede3f4257 --- /dev/null +++ b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs @@ -0,0 +1,18 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] + +#![allow(non_camel_case_types, incomplete_features)] +#![feature(repr_simd, platform_intrinsics, const_generics)] + +#[repr(simd)] +#[derive(Copy, Clone)] +pub struct S([f32; N]); + +// CHECK-LABEL: @build_array +#[no_mangle] +pub fn build_array(x: [f32; 4]) -> S<4> { + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %3, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %{{[0-9]+}}, i8* align 4 %6, i64 16, i1 false) + S::<4>(x) +} diff --git a/src/test/ui/simd/simd-array-type.rs b/src/test/ui/simd/simd-array-type.rs new file mode 100644 index 0000000000000..c84988cffb423 --- /dev/null +++ b/src/test/ui/simd/simd-array-type.rs @@ -0,0 +1,44 @@ +// run-pass +#![allow(dead_code, incomplete_features)] + +// pretty-expanded FIXME #23616 + +#![feature(repr_simd)] +#![feature(platform_intrinsics)] +#![feature(const_generics)] + +#[repr(simd)] +#[derive(Copy, Clone)] +struct S([i32; 4]); + +#[repr(simd)] +#[derive(Copy, Clone)] +struct T([i32; N]); + +extern "platform-intrinsic" { + fn simd_insert(x: T, idx: u32, y: E) -> T; + fn simd_extract(x: T, idx: u32) -> E; +} + +pub fn main() { + let mut s = S([0; 4]); + + unsafe { + for i in 0_i32..4 { + s = simd_insert(s, i as u32, i); + } + for i in 0_i32..4 { + assert_eq!(i, simd_extract(s, i as u32)); + } + } + + let mut t = T::<4>([0; 4]); + unsafe { + for i in 0_i32..4 { + t = simd_insert(t, i as u32, i); + } + for i in 0_i32..4 { + assert_eq!(i, simd_extract(t, i as u32)); + } + } +} diff --git a/src/test/ui/simd/simd-generics.rs b/src/test/ui/simd/simd-generics.rs index ab6caee9d7bce..9877e37dfb2ec 100644 --- a/src/test/ui/simd/simd-generics.rs +++ b/src/test/ui/simd/simd-generics.rs @@ -1,9 +1,6 @@ // run-pass -#![allow(non_camel_case_types)] - - - -#![feature(repr_simd, platform_intrinsics)] +#![allow(non_camel_case_types, incomplete_features)] +#![feature(repr_simd, platform_intrinsics, const_generics)] use std::ops; @@ -11,6 +8,11 @@ use std::ops; #[derive(Copy, Clone)] struct f32x4(f32, f32, f32, f32); +#[repr(simd)] +#[derive(Copy, Clone)] +struct S([f32; N]); + + extern "platform-intrinsic" { fn simd_add(x: T, y: T) -> T; } @@ -27,7 +29,16 @@ impl ops::Add for f32x4 { } } -pub fn main() { +impl ops::Add for S<4> { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + unsafe {simd_add(self, rhs)} + } +} + + +pub fn main() { unsafe { let lr = f32x4(1.0f32, 2.0f32, 3.0f32, 4.0f32); // lame-o @@ -36,4 +47,11 @@ pub fn main() { assert_eq!(y, 4.0f32); assert_eq!(z, 6.0f32); assert_eq!(w, 8.0f32); -} + + let lr2 = S::<4>([1.0f32, 2.0f32, 3.0f32, 4.0f32]); + let [x, y, z, w] = add(lr2, lr2).0; + assert_eq!(x, 2.0f32); + assert_eq!(y, 4.0f32); + assert_eq!(z, 6.0f32); + assert_eq!(w, 8.0f32); +}} diff --git a/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs b/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs index b2ddcf023ebcb..c7bb895197741 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-arithmetic-saturating.rs @@ -2,16 +2,16 @@ // ignore-emscripten // min-llvm-version 8.0 -#![allow(non_camel_case_types)] -#![feature(repr_simd, platform_intrinsics)] +#![allow(non_camel_case_types, incomplete_features)] +#![feature(repr_simd, platform_intrinsics, const_generics)] #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] struct u32x4(pub u32, pub u32, pub u32, pub u32); #[repr(simd)] -#[derive(Copy, Clone, PartialEq, Debug)] -struct i32x4(pub i32, pub i32, pub i32, pub i32); +#[derive(Copy, Clone)] +struct I32([i32; N]); extern "platform-intrinsic" { fn simd_saturating_add(x: T, y: T) -> T; @@ -52,41 +52,41 @@ fn main() { const MIN: i32 = i32::min_value(); const MAX: i32 = i32::max_value(); - let a = i32x4(1, 2, 3, 4); - let b = i32x4(2, 4, 6, 8); - let c = i32x4(-1, -2, -3, -4); - let d = i32x4(-2, -4, -6, -8); + let a = I32::<4>([1, 2, 3, 4]); + let b = I32::<4>([2, 4, 6, 8]); + let c = I32::<4>([-1, -2, -3, -4]); + let d = I32::<4>([-2, -4, -6, -8]); - let max = i32x4(MAX, MAX, MAX, MAX); - let max1 = i32x4(MAX - 1, MAX - 1, MAX - 1, MAX - 1); - let min = i32x4(MIN, MIN, MIN, MIN); - let min1 = i32x4(MIN + 1, MIN + 1, MIN + 1, MIN + 1); + let max = I32::<4>([MAX, MAX, MAX, MAX]); + let max1 = I32::<4>([MAX - 1, MAX - 1, MAX - 1, MAX - 1]); + let min = I32::<4>([MIN, MIN, MIN, MIN]); + let min1 = I32::<4>([MIN + 1, MIN + 1, MIN + 1, MIN + 1]); - let z = i32x4(0, 0, 0, 0); + let z = I32::<4>([0, 0, 0, 0]); unsafe { - assert_eq!(simd_saturating_add(z, z), z); - assert_eq!(simd_saturating_add(z, a), a); - assert_eq!(simd_saturating_add(b, z), b); - assert_eq!(simd_saturating_add(a, a), b); - assert_eq!(simd_saturating_add(a, max), max); - assert_eq!(simd_saturating_add(max, b), max); - assert_eq!(simd_saturating_add(max1, a), max); - assert_eq!(simd_saturating_add(min1, z), min1); - assert_eq!(simd_saturating_add(min, z), min); - assert_eq!(simd_saturating_add(min1, c), min); - assert_eq!(simd_saturating_add(min, c), min); - assert_eq!(simd_saturating_add(min1, d), min); - assert_eq!(simd_saturating_add(min, d), min); + assert_eq!(simd_saturating_add(z, z).0, z.0); + assert_eq!(simd_saturating_add(z, a).0, a.0); + assert_eq!(simd_saturating_add(b, z).0, b.0); + assert_eq!(simd_saturating_add(a, a).0, b.0); + assert_eq!(simd_saturating_add(a, max).0, max.0); + assert_eq!(simd_saturating_add(max, b).0, max.0); + assert_eq!(simd_saturating_add(max1, a).0, max.0); + assert_eq!(simd_saturating_add(min1, z).0, min1.0); + assert_eq!(simd_saturating_add(min, z).0, min.0); + assert_eq!(simd_saturating_add(min1, c).0, min.0); + assert_eq!(simd_saturating_add(min, c).0, min.0); + assert_eq!(simd_saturating_add(min1, d).0, min.0); + assert_eq!(simd_saturating_add(min, d).0, min.0); - assert_eq!(simd_saturating_sub(b, z), b); - assert_eq!(simd_saturating_sub(b, a), a); - assert_eq!(simd_saturating_sub(a, a), z); - assert_eq!(simd_saturating_sub(a, b), c); - assert_eq!(simd_saturating_sub(z, max), min1); - assert_eq!(simd_saturating_sub(min1, z), min1); - assert_eq!(simd_saturating_sub(min1, a), min); - assert_eq!(simd_saturating_sub(min1, b), min); + assert_eq!(simd_saturating_sub(b, z).0, b.0); + assert_eq!(simd_saturating_sub(b, a).0, a.0); + assert_eq!(simd_saturating_sub(a, a).0, z.0); + assert_eq!(simd_saturating_sub(a, b).0, c.0); + assert_eq!(simd_saturating_sub(z, max).0, min1.0); + assert_eq!(simd_saturating_sub(min1, z).0, min1.0); + assert_eq!(simd_saturating_sub(min1, a).0, min.0); + assert_eq!(simd_saturating_sub(min1, b).0, min.0); } } } diff --git a/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs b/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs index b67c0ad1eb2f1..83f11c6abdbf0 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-arithmetic.rs @@ -1,9 +1,9 @@ // run-pass -#![allow(non_camel_case_types)] +#![allow(non_camel_case_types, incomplete_features)] // ignore-emscripten FIXME(#45351) hits an LLVM assert -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, platform_intrinsics, const_generics)] #[repr(simd)] #[derive(Copy, Clone)] @@ -11,7 +11,7 @@ struct i32x4(pub i32, pub i32, pub i32, pub i32); #[repr(simd)] #[derive(Copy, Clone)] -struct u32x4(pub u32, pub u32, pub u32, pub u32); +struct U32([u32; N]); #[repr(simd)] #[derive(Copy, Clone)] @@ -25,6 +25,15 @@ macro_rules! all_eq { }} } +macro_rules! all_eq_ { + ($a: expr, $b: expr) => {{ + let a = $a; + let b = $b; + assert!(a.0 == b.0); + }} +} + + extern "platform-intrinsic" { fn simd_add(x: T, y: T) -> T; fn simd_sub(x: T, y: T) -> T; @@ -40,81 +49,81 @@ extern "platform-intrinsic" { fn main() { let x1 = i32x4(1, 2, 3, 4); - let y1 = u32x4(1, 2, 3, 4); + let y1 = U32::<4>([1, 2, 3, 4]); let z1 = f32x4(1.0, 2.0, 3.0, 4.0); let x2 = i32x4(2, 3, 4, 5); - let y2 = u32x4(2, 3, 4, 5); + let y2 = U32::<4>([2, 3, 4, 5]); let z2 = f32x4(2.0, 3.0, 4.0, 5.0); unsafe { all_eq!(simd_add(x1, x2), i32x4(3, 5, 7, 9)); all_eq!(simd_add(x2, x1), i32x4(3, 5, 7, 9)); - all_eq!(simd_add(y1, y2), u32x4(3, 5, 7, 9)); - all_eq!(simd_add(y2, y1), u32x4(3, 5, 7, 9)); + all_eq_!(simd_add(y1, y2), U32::<4>([3, 5, 7, 9])); + all_eq_!(simd_add(y2, y1), U32::<4>([3, 5, 7, 9])); all_eq!(simd_add(z1, z2), f32x4(3.0, 5.0, 7.0, 9.0)); all_eq!(simd_add(z2, z1), f32x4(3.0, 5.0, 7.0, 9.0)); all_eq!(simd_mul(x1, x2), i32x4(2, 6, 12, 20)); all_eq!(simd_mul(x2, x1), i32x4(2, 6, 12, 20)); - all_eq!(simd_mul(y1, y2), u32x4(2, 6, 12, 20)); - all_eq!(simd_mul(y2, y1), u32x4(2, 6, 12, 20)); + all_eq_!(simd_mul(y1, y2), U32::<4>([2, 6, 12, 20])); + all_eq_!(simd_mul(y2, y1), U32::<4>([2, 6, 12, 20])); all_eq!(simd_mul(z1, z2), f32x4(2.0, 6.0, 12.0, 20.0)); all_eq!(simd_mul(z2, z1), f32x4(2.0, 6.0, 12.0, 20.0)); all_eq!(simd_sub(x2, x1), i32x4(1, 1, 1, 1)); all_eq!(simd_sub(x1, x2), i32x4(-1, -1, -1, -1)); - all_eq!(simd_sub(y2, y1), u32x4(1, 1, 1, 1)); - all_eq!(simd_sub(y1, y2), u32x4(!0, !0, !0, !0)); + all_eq_!(simd_sub(y2, y1), U32::<4>([1, 1, 1, 1])); + all_eq_!(simd_sub(y1, y2), U32::<4>([!0, !0, !0, !0])); all_eq!(simd_sub(z2, z1), f32x4(1.0, 1.0, 1.0, 1.0)); all_eq!(simd_sub(z1, z2), f32x4(-1.0, -1.0, -1.0, -1.0)); all_eq!(simd_div(x1, x1), i32x4(1, 1, 1, 1)); all_eq!(simd_div(i32x4(2, 4, 6, 8), i32x4(2, 2, 2, 2)), x1); - all_eq!(simd_div(y1, y1), u32x4(1, 1, 1, 1)); - all_eq!(simd_div(u32x4(2, 4, 6, 8), u32x4(2, 2, 2, 2)), y1); + all_eq_!(simd_div(y1, y1), U32::<4>([1, 1, 1, 1])); + all_eq_!(simd_div(U32::<4>([2, 4, 6, 8]), U32::<4>([2, 2, 2, 2])), y1); all_eq!(simd_div(z1, z1), f32x4(1.0, 1.0, 1.0, 1.0)); all_eq!(simd_div(z1, z2), f32x4(1.0/2.0, 2.0/3.0, 3.0/4.0, 4.0/5.0)); all_eq!(simd_div(z2, z1), f32x4(2.0/1.0, 3.0/2.0, 4.0/3.0, 5.0/4.0)); all_eq!(simd_rem(x1, x1), i32x4(0, 0, 0, 0)); all_eq!(simd_rem(x2, x1), i32x4(0, 1, 1, 1)); - all_eq!(simd_rem(y1, y1), u32x4(0, 0, 0, 0)); - all_eq!(simd_rem(y2, y1), u32x4(0, 1, 1, 1)); + all_eq_!(simd_rem(y1, y1), U32::<4>([0, 0, 0, 0])); + all_eq_!(simd_rem(y2, y1), U32::<4>([0, 1, 1, 1])); all_eq!(simd_rem(z1, z1), f32x4(0.0, 0.0, 0.0, 0.0)); all_eq!(simd_rem(z1, z2), z1); all_eq!(simd_rem(z2, z1), f32x4(0.0, 1.0, 1.0, 1.0)); all_eq!(simd_shl(x1, x2), i32x4(1 << 2, 2 << 3, 3 << 4, 4 << 5)); all_eq!(simd_shl(x2, x1), i32x4(2 << 1, 3 << 2, 4 << 3, 5 << 4)); - all_eq!(simd_shl(y1, y2), u32x4(1 << 2, 2 << 3, 3 << 4, 4 << 5)); - all_eq!(simd_shl(y2, y1), u32x4(2 << 1, 3 << 2, 4 << 3, 5 << 4)); + all_eq_!(simd_shl(y1, y2), U32::<4>([1 << 2, 2 << 3, 3 << 4, 4 << 5])); + all_eq_!(simd_shl(y2, y1), U32::<4>([2 << 1, 3 << 2, 4 << 3, 5 << 4])); // test right-shift by assuming left-shift is correct all_eq!(simd_shr(simd_shl(x1, x2), x2), x1); all_eq!(simd_shr(simd_shl(x2, x1), x1), x2); - all_eq!(simd_shr(simd_shl(y1, y2), y2), y1); - all_eq!(simd_shr(simd_shl(y2, y1), y1), y2); + all_eq_!(simd_shr(simd_shl(y1, y2), y2), y1); + all_eq_!(simd_shr(simd_shl(y2, y1), y1), y2); // ensure we get logical vs. arithmetic shifts correct let (a, b, c, d) = (-12, -123, -1234, -12345); all_eq!(simd_shr(i32x4(a, b, c, d), x1), i32x4(a >> 1, b >> 2, c >> 3, d >> 4)); - all_eq!(simd_shr(u32x4(a as u32, b as u32, c as u32, d as u32), y1), - u32x4((a as u32) >> 1, (b as u32) >> 2, (c as u32) >> 3, (d as u32) >> 4)); + all_eq_!(simd_shr(U32::<4>([a as u32, b as u32, c as u32, d as u32]), y1), + U32::<4>([(a as u32) >> 1, (b as u32) >> 2, (c as u32) >> 3, (d as u32) >> 4])); all_eq!(simd_and(x1, x2), i32x4(0, 2, 0, 4)); all_eq!(simd_and(x2, x1), i32x4(0, 2, 0, 4)); - all_eq!(simd_and(y1, y2), u32x4(0, 2, 0, 4)); - all_eq!(simd_and(y2, y1), u32x4(0, 2, 0, 4)); + all_eq_!(simd_and(y1, y2), U32::<4>([0, 2, 0, 4])); + all_eq_!(simd_and(y2, y1), U32::<4>([0, 2, 0, 4])); all_eq!(simd_or(x1, x2), i32x4(3, 3, 7, 5)); all_eq!(simd_or(x2, x1), i32x4(3, 3, 7, 5)); - all_eq!(simd_or(y1, y2), u32x4(3, 3, 7, 5)); - all_eq!(simd_or(y2, y1), u32x4(3, 3, 7, 5)); + all_eq_!(simd_or(y1, y2), U32::<4>([3, 3, 7, 5])); + all_eq_!(simd_or(y2, y1), U32::<4>([3, 3, 7, 5])); all_eq!(simd_xor(x1, x2), i32x4(3, 1, 7, 1)); all_eq!(simd_xor(x2, x1), i32x4(3, 1, 7, 1)); - all_eq!(simd_xor(y1, y2), u32x4(3, 1, 7, 1)); - all_eq!(simd_xor(y2, y1), u32x4(3, 1, 7, 1)); + all_eq_!(simd_xor(y1, y2), U32::<4>([3, 1, 7, 1])); + all_eq_!(simd_xor(y2, y1), U32::<4>([3, 1, 7, 1])); } } From 3773bcf6ee32ff432c67583172342a14ba8e475b Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Tue, 13 Aug 2019 21:51:27 +0200 Subject: [PATCH 02/13] Document which errors are caught in typeck --- src/librustc/ty/layout.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 859b6bcc65a1d..5fff763ffc247 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -696,7 +696,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // // where T is a "machine type", e.g., `f32`, `i64`, `*mut _`. - // SIMD vectors with zero fields are not supported: + // SIMD vectors with zero fields are not supported. + // (should be caught by typeck) if def.non_enum_variant().fields.is_empty() { tcx.sess.fatal(&format!( "monomorphising SIMD type `{}` of zero length", ty @@ -707,6 +708,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let f0_ty = def.non_enum_variant().fields[0].ty(tcx, substs); // Heterogeneous SIMD vectors are not supported: + // (should be caught by typeck) for fi in &def.non_enum_variant().fields { if fi.ty(tcx, substs) != f0_ty { tcx.sess.fatal(&format!( @@ -726,6 +728,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // First ADT field is an array: // SIMD vectors with multiple array fields are not supported: + // (should be caught by typeck) if def.non_enum_variant().fields.len() != 1 { tcx.sess.fatal(&format!( "monomorphising SIMD type `{}` with more than one array field", @@ -752,7 +755,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { (f0_ty, def.non_enum_variant().fields.len() as _, false) }; - // SIMD vectors of zero length are not supported: + // SIMD vectors of zero length are not supported. + // + // Can't be caught in typeck if the array length is generic. if e_len == 0 { tcx.sess.fatal(&format!( "monomorphising SIMD type `{}` of zero length", ty @@ -764,6 +769,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let e_abi = if let Abi::Scalar(ref scalar) = e_ly.abi { scalar.clone() } else { + // This error isn;t caught in typeck, e.g., if + // the element type of the vector is generic. tcx.sess.fatal(&format!( "monomorphising SIMD type `{}` with a non-machine element type `{}`", ty, e_ty From 58fe6835bdc1ddb80a3be81872c7f5759f4eda3c Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 14 Aug 2019 07:13:02 +0200 Subject: [PATCH 03/13] Ignore tidy length in simd-array-transmute test --- .../codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs index 4520ede3f4257..15af67ad77263 100644 --- a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs +++ b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] From 896396619239bd84f4740d2142b86e5904f722c8 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 14 Aug 2019 09:19:18 +0200 Subject: [PATCH 04/13] Rename simd_type_size to simd_ty_and_len --- src/librustc_codegen_llvm/intrinsic.rs | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 65996c4980acb..d2250c8c3d3f6 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -1041,7 +1041,7 @@ fn generic_simd_intrinsic( ) -> Result<&'ll Value, ()> { // Given a SIMD vector type `x` return the element type and the number of // elements in the vector. - fn simd_type_size(bx: &Builder<'a, 'll, 'tcx>, x: Ty<'tcx>) -> (Ty<'tcx>, usize) { + fn simd_ty_and_len(bx: &Builder<'a, 'll, 'tcx>, x: Ty<'tcx>) -> (Ty<'tcx>, usize) { let ty = if let ty::Adt(def, substs) = x.sty { assert!(def.repr.simd()); let field0_ty = def.non_enum_variant().fields[0].ty(bx.tcx(), substs); @@ -1118,7 +1118,7 @@ fn generic_simd_intrinsic( require_simd!(arg_tys[1], "argument"); - let (_, v_len) = simd_type_size(bx, arg_tys[1]); + let (_, v_len) = simd_ty_and_len(bx, arg_tys[1]); require!(m_len == v_len, "mismatched lengths: mask length `{}` != other vector length `{}`", m_len, v_len @@ -1143,11 +1143,11 @@ fn generic_simd_intrinsic( _ => None }; - let (in_elem, in_len) = simd_type_size(bx, arg_tys[0]); + 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_ty, out_len) = simd_type_size(bx, ret_ty); + 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 `{}`), \ @@ -1175,7 +1175,7 @@ fn generic_simd_intrinsic( require_simd!(ret_ty, "return"); - let (out_ty, out_len) = simd_type_size(bx, ret_ty); + let (out_ty, out_len) = simd_ty_and_len(bx, ret_ty); require!(out_len == n, "expected return type of length {}, found `{}` with length {}", n, ret_ty, out_len); @@ -1236,7 +1236,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) = simd_type_size(bx, arg_tys[1]); + let (_, v_len) = simd_ty_and_len(bx, arg_tys[1]); require!(m_len == v_len, "mismatched lengths: mask length `{}` != other vector length `{}`", m_len, v_len @@ -1452,8 +1452,8 @@ fn generic_simd_intrinsic( require_simd!(ret_ty, "return"); // Of the same length: - let (_, out_len) = simd_type_size(bx, arg_tys[1]); - let (_, out_len2) = simd_type_size(bx, arg_tys[2]); + 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 == out_len, "expected {} argument with length {} (same as input type `{}`), \ found `{}` with length {}", "second", in_len, in_ty, arg_tys[1], @@ -1486,8 +1486,8 @@ 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 (element_ty0, _) = simd_type_size(bx, arg_tys[0]); - let (element_ty1, _) = simd_type_size(bx, arg_tys[1]); + 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.sty { ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), @@ -1505,7 +1505,7 @@ fn generic_simd_intrinsic( assert_eq!(underlying_ty, non_ptr(element_ty0)); // The element type of the third argument must be a signed integer type of any width: - let (element_ty2, _) = simd_type_size(bx, arg_tys[2]); + let (element_ty2, _) = simd_ty_and_len(bx, arg_tys[2]); match element_ty2.sty { ty::Int(_) => (), _ => { @@ -1561,8 +1561,8 @@ fn generic_simd_intrinsic( require_simd!(arg_tys[2], "third"); // Of the same length: - let (_, element_len1) = simd_type_size(bx, arg_tys[1]); - let (_, element_len2) = simd_type_size(bx, arg_tys[2]); + 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 == element_len1, "expected {} argument with length {} (same as input type `{}`), \ found `{}` with length {}", "second", in_len, in_ty, arg_tys[1], @@ -1590,9 +1590,9 @@ 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 (element_ty0, _element_len0) = simd_type_size(bx, arg_tys[0]); - let (element_ty1, _element_len1) = simd_type_size(bx, arg_tys[1]); - let (element_ty2, _element_len2) = simd_type_size(bx, arg_tys[2]); + 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.sty { ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::MutMutable => (ptr_count(element_ty1), @@ -1793,7 +1793,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, if name == "simd_cast" { require_simd!(ret_ty, "return"); - let (out_elem, out_len) = simd_type_size(bx, ret_ty); + 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 `{}`), \ found `{}` with length {}", @@ -1913,7 +1913,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, return_error!( "expected element type `{}` of vector type `{}` \ to be a signed or unsigned integer type", - simd_type_size(bx, arg_tys[0]).0, arg_tys[0] + simd_ty_and_len(bx, arg_tys[0]).0, arg_tys[0] ); } }; From bf448098e6e25ffa757b6cd74f2c1e1eb4fdf50c Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 14 Aug 2019 09:22:49 +0200 Subject: [PATCH 05/13] Remove assert --- src/librustc_codegen_llvm/intrinsic.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index d2250c8c3d3f6..a1607a10efdbc 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -1043,7 +1043,6 @@ fn generic_simd_intrinsic( // elements in the vector. fn simd_ty_and_len(bx: &Builder<'a, 'll, 'tcx>, x: Ty<'tcx>) -> (Ty<'tcx>, usize) { let ty = if let ty::Adt(def, substs) = x.sty { - assert!(def.repr.simd()); let field0_ty = def.non_enum_variant().fields[0].ty(bx.tcx(), substs); if let ty::Array(element_ty, _) = field0_ty.sty { element_ty From a446967fe2cd39c91ccafc96909b2e309516b088 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 14 Aug 2019 09:26:56 +0200 Subject: [PATCH 06/13] Revert unnecessary param_env change --- src/librustc_codegen_llvm/intrinsic.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index a1607a10efdbc..c7be39be4167a 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -1098,9 +1098,8 @@ fn generic_simd_intrinsic( } let tcx = bx.tcx(); - let param_env = ty::ParamEnv::reveal_all(); let sig = tcx.normalize_erasing_late_bound_regions( - param_env, + ty::ParamEnv::reveal_all(), &callee_ty.fn_sig(tcx), ); let arg_tys = sig.inputs(); From 518eff215a8c086b868825f3101939bc60151c8c Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 14 Aug 2019 17:42:52 +0200 Subject: [PATCH 07/13] Improve non-machine type in error message --- src/librustc/ty/layout.rs | 6 +++--- src/test/ui/simd-type-generic-monomorphisation.rs | 4 +++- src/test/ui/simd-type-generic-monomorphisation.stderr | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 5fff763ffc247..37874fe6848ff 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -694,7 +694,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // * #[repr(simd)] struct S { x: T, y: T, z: T, w: T } // * #[repr(simd)] struct S([T; 4]) // - // where T is a "machine type", e.g., `f32`, `i64`, `*mut _`. + // where T is a primitive scalar (integer/float/pointer). // SIMD vectors with zero fields are not supported. // (should be caught by typeck) @@ -746,7 +746,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { }) = self.layout_of(f0_ty) { count } else { - unreachable!(); + return Err(LayoutError::Unknown(ty)); }; (e_ty, *len, true) @@ -772,7 +772,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // This error isn;t caught in typeck, e.g., if // the element type of the vector is generic. tcx.sess.fatal(&format!( - "monomorphising SIMD type `{}` with a non-machine element type `{}`", + "monomorphising SIMD type `{}` with a non-primitive-scalar (integer/float/pointer) element type `{}`", ty, e_ty )) }; diff --git a/src/test/ui/simd-type-generic-monomorphisation.rs b/src/test/ui/simd-type-generic-monomorphisation.rs index 68eb78759ebd7..3bdada9197f82 100644 --- a/src/test/ui/simd-type-generic-monomorphisation.rs +++ b/src/test/ui/simd-type-generic-monomorphisation.rs @@ -1,6 +1,8 @@ #![feature(repr_simd, platform_intrinsics)] -// error-pattern:monomorphising SIMD type `Simd2` with a non-machine element type `X` +// ignore-tidy-linelength + +// error-pattern:monomorphising SIMD type `Simd2` with a non-primitive-scalar (integer/float/pointer) element type `X` struct X(Vec); #[repr(simd)] diff --git a/src/test/ui/simd-type-generic-monomorphisation.stderr b/src/test/ui/simd-type-generic-monomorphisation.stderr index 2a74506e80ea5..7f23893ac8536 100644 --- a/src/test/ui/simd-type-generic-monomorphisation.stderr +++ b/src/test/ui/simd-type-generic-monomorphisation.stderr @@ -1,4 +1,4 @@ -error: monomorphising SIMD type `Simd2` with a non-machine element type `X` +error: monomorphising SIMD type `Simd2` with a non-primitive-scalar (integer/float/pointer) element type `X` error: aborting due to previous error From fbe64f42f5617ab00e4d38285bd81d62c7fc5af0 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 14 Aug 2019 17:43:03 +0200 Subject: [PATCH 08/13] Add more transmute tests --- .../simd-intrinsic-transmute-array.rs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs index 15af67ad77263..98fb6921d7563 100644 --- a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs +++ b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs @@ -10,10 +10,34 @@ #[derive(Copy, Clone)] pub struct S([f32; N]); -// CHECK-LABEL: @build_array +#[repr(simd)] +#[derive(Copy, Clone)] +pub struct T([f32; 4]); + +#[repr(simd)] +#[derive(Copy, Clone)] +pub struct U(f32, f32, f32, f32); + +// CHECK-LABEL: @build_array_s #[no_mangle] -pub fn build_array(x: [f32; 4]) -> S<4> { +pub fn build_array_s(x: [f32; 4]) -> S<4> { // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %3, i64 16, i1 false) // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %{{[0-9]+}}, i8* align 4 %6, i64 16, i1 false) S::<4>(x) } + +// CHECK-LABEL: @build_array_t +#[no_mangle] +pub fn build_array_t(x: [f32; 4]) -> T { + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %3, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %{{[0-9]+}}, i8* align 4 %6, i64 16, i1 false) + T(x) +} + +// CHECK-LABEL: @build_array_u +#[no_mangle] +pub fn build_array_u(x: [f32; 4]) -> U { + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %3, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %6, i64 16, i1 false) + unsafe { std::mem::transmute(x) } +} From 173aafa8279a41f9b0f6f374b4705e5f446aeecd Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 15 Aug 2019 16:19:00 +0200 Subject: [PATCH 09/13] Fix more tests --- src/librustc/ty/layout.rs | 3 ++- src/librustc_codegen_llvm/intrinsic.rs | 12 ++++++------ src/librustc_typeck/check/mod.rs | 7 +++++-- src/test/ui/error-codes/E0077.stderr | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 37874fe6848ff..4228c953f749b 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -772,7 +772,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // This error isn;t caught in typeck, e.g., if // the element type of the vector is generic. tcx.sess.fatal(&format!( - "monomorphising SIMD type `{}` with a non-primitive-scalar (integer/float/pointer) element type `{}`", + "monomorphising SIMD type `{}` with a non-primitive-scalar \ + (integer/float/pointer) element type `{}`", ty, e_ty )) }; diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index c7be39be4167a..abfb116b898b0 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -1041,18 +1041,18 @@ fn generic_simd_intrinsic( ) -> 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>, x: Ty<'tcx>) -> (Ty<'tcx>, usize) { - let ty = if let ty::Adt(def, substs) = x.sty { - let field0_ty = def.non_enum_variant().fields[0].ty(bx.tcx(), substs); - if let ty::Array(element_ty, _) = field0_ty.sty { + fn simd_ty_and_len(bx: &Builder<'a, 'll, 'tcx>, simd_ty: Ty<'tcx>) -> (Ty<'tcx>, usize) { + let ty = if let ty::Adt(def, substs) = simd_ty.sty { + let f0_ty = def.non_enum_variant().fields[0].ty(bx.tcx(), substs); + if let ty::Array(element_ty, _) = f0_ty.sty { element_ty } else { - field0_ty + f0_ty } } else { bug!("should only be called with a SIMD type") }; - let count = if let ty::layout::Abi::Vector{count, ..} = bx.layout_of(x).abi { + let count = if let ty::layout::Abi::Vector{count, ..} = bx.layout_of(simd_ty).abi { count } else { bug!("should only be called with a SIMD type") diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a9ff1eb6c5fdf..2ec3c9416b139 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1763,8 +1763,11 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) { _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ } ty::Array(ty, _c) if ty.is_machine() => { /* struct([f32; 4]) */ } _ => { - span_err!(tcx.sess, sp, E0077, - "SIMD vector element type should be machine type"); + span_err!( + tcx.sess, sp, E0077, + "SIMD vector element type should be a \ + primitive scalar (integer/float/pointer) type" + ); return; } } diff --git a/src/test/ui/error-codes/E0077.stderr b/src/test/ui/error-codes/E0077.stderr index 4f85d175ce65d..1938a9a272a16 100644 --- a/src/test/ui/error-codes/E0077.stderr +++ b/src/test/ui/error-codes/E0077.stderr @@ -1,4 +1,4 @@ -error[E0077]: SIMD vector element type should be machine type +error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type --> $DIR/E0077.rs:4:1 | LL | struct Bad(String); From 9f72fc7cd7c56dfa4fb6715af4daf2045188d982 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 28 Aug 2019 14:30:40 +0200 Subject: [PATCH 10/13] Simplify simd_ty_and_len --- src/librustc_codegen_llvm/intrinsic.rs | 4 ++-- src/test/ui/simd-type.rs | 10 ++++++++++ src/test/ui/simd-type.stderr | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index abfb116b898b0..254436e117514 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -1042,8 +1042,8 @@ fn generic_simd_intrinsic( // 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>, usize) { - let ty = if let ty::Adt(def, substs) = simd_ty.sty { - let f0_ty = def.non_enum_variant().fields[0].ty(bx.tcx(), substs); + let ty = if let ty::Adt(_def, _substs) = simd_ty.sty { + let f0_ty = bx.layout_of(simd_ty).field(bx, 0).ty; if let ty::Array(element_ty, _) = f0_ty.sty { element_ty } else { diff --git a/src/test/ui/simd-type.rs b/src/test/ui/simd-type.rs index 9e4b7e7656055..a320df85138e5 100644 --- a/src/test/ui/simd-type.rs +++ b/src/test/ui/simd-type.rs @@ -1,10 +1,20 @@ #![feature(repr_simd)] #![allow(non_camel_case_types)] +// ignore-tidy-linelength + #[repr(simd)] struct empty; //~ ERROR SIMD vector cannot be empty #[repr(simd)] struct i64f64(i64, f64); //~ ERROR SIMD vector should be homogeneous +struct Foo; + +#[repr(simd)] +struct FooV(Foo, Foo); //~ ERROR SIMD vector element type should be a primitive scalar (integer/float/pointer) type + +#[repr(simd)] +struct FooV2([Foo; 2]); //~ ERROR SIMD vector element type should be a primitive scalar (integer/float/pointer) type + fn main() {} diff --git a/src/test/ui/simd-type.stderr b/src/test/ui/simd-type.stderr index 0c4242f46b786..23004c785918c 100644 --- a/src/test/ui/simd-type.stderr +++ b/src/test/ui/simd-type.stderr @@ -1,16 +1,28 @@ error[E0075]: SIMD vector cannot be empty - --> $DIR/simd-type.rs:5:1 + --> $DIR/simd-type.rs:7:1 | LL | struct empty; | ^^^^^^^^^^^^^ error[E0076]: SIMD vector should be homogeneous - --> $DIR/simd-type.rs:8:1 + --> $DIR/simd-type.rs:10:1 | LL | struct i64f64(i64, f64); | ^^^^^^^^^^^^^^^^^^^^^^^^ SIMD elements must have the same type -error: aborting due to 2 previous errors +error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type + --> $DIR/simd-type.rs:15:1 + | +LL | struct FooV(Foo, Foo); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type + --> $DIR/simd-type.rs:18:1 + | +LL | struct FooV2([Foo; 2]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0075, E0076. +Some errors have detailed explanations: E0075, E0076, E0077. For more information about an error, try `rustc --explain E0075`. From 06037a3ce56aa244f69fe6c69794a7a6951c29ac Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Wed, 28 Aug 2019 14:44:24 +0200 Subject: [PATCH 11/13] Fix typo --- src/librustc/ty/layout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 4228c953f749b..845bee87a7ad6 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -769,7 +769,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let e_abi = if let Abi::Scalar(ref scalar) = e_ly.abi { scalar.clone() } else { - // This error isn;t caught in typeck, e.g., if + // This error isn't caught in typeck, e.g., if // the element type of the vector is generic. tcx.sess.fatal(&format!( "monomorphising SIMD type `{}` with a non-primitive-scalar \ From 69d0fa97c3233c843e246c9c1302c9af7d2940e7 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Thu, 29 Aug 2019 20:37:45 +0200 Subject: [PATCH 12/13] Skip align attribute in tests --- .../simd-intrinsic/simd-intrinsic-transmute-array.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs index 98fb6921d7563..eeab44cf049bf 100644 --- a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs +++ b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs @@ -21,23 +21,23 @@ pub struct U(f32, f32, f32, f32); // CHECK-LABEL: @build_array_s #[no_mangle] pub fn build_array_s(x: [f32; 4]) -> S<4> { - // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %3, i64 16, i1 false) - // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %{{[0-9]+}}, i8* align 4 %6, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false) S::<4>(x) } // CHECK-LABEL: @build_array_t #[no_mangle] pub fn build_array_t(x: [f32; 4]) -> T { - // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %3, i64 16, i1 false) - // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %{{[0-9]+}}, i8* align 4 %6, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false) T(x) } // CHECK-LABEL: @build_array_u #[no_mangle] pub fn build_array_u(x: [f32; 4]) -> U { - // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %3, i64 16, i1 false) - // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %{{[0-9]+}}, i8* align 4 %6, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false) + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false) unsafe { std::mem::transmute(x) } } From a21939b91a58cc154c414e2520ee3009de887913 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Fri, 30 Aug 2019 02:03:08 +0200 Subject: [PATCH 13/13] Bump min-llvm-version of transmute test --- .../codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs index eeab44cf049bf..23098e7f64945 100644 --- a/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs +++ b/src/test/codegen/simd-intrinsic/simd-intrinsic-transmute-array.rs @@ -1,5 +1,6 @@ // ignore-tidy-linelength // compile-flags: -C no-prepopulate-passes +// min-llvm-version 8.0 #![crate_type = "lib"]