Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 54 additions & 54 deletions library/core/src/intrinsics/simd.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/tools/miri/tests/pass/intrinsics/portable-simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::simd::prelude::*;

#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn simd_shuffle_const_generic<T, U, const IDX: &'static [u32]>(x: T, y: T) -> U;
pub const unsafe fn simd_shuffle_const_generic<T, U, const IDX: &'static [u32]>(x: T, y: T) -> U;

fn simd_ops_f32() {
let a = f32x4::splat(10.0);
Expand Down
130 changes: 106 additions & 24 deletions tests/auxiliary/minisimd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#![allow(unused)]
#![allow(non_camel_case_types)]
#![allow(unexpected_cfgs)]

// The field is currently left `pub` for convenience in porting tests, many of
// which attempt to just construct it directly. That still works; it's just the
Expand All @@ -24,39 +25,32 @@ impl<T: Copy, const N: usize> Clone for Simd<T, N> {
}
}

impl<T: PartialEq, const N: usize> PartialEq for Simd<T, N> {
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}

impl<T: core::fmt::Debug, const N: usize> core::fmt::Debug for Simd<T, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
<[T; N] as core::fmt::Debug>::fmt(self.as_array(), f)
}
}

impl<T, const N: usize> core::ops::Index<usize> for Simd<T, N> {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_array()[i]
}
}

impl<T, const N: usize> Simd<T, N> {
pub const fn from_array(a: [T; N]) -> Self {
Simd(a)
}
pub fn as_array(&self) -> &[T; N] {
pub const fn as_array(&self) -> &[T; N] {
let p: *const Self = self;
unsafe { &*p.cast::<[T; N]>() }
}
pub fn into_array(self) -> [T; N]
pub const fn into_array(self) -> [T; N]
where
T: Copy,
{
*self.as_array()
}
pub const fn splat(a: T) -> Self
where
T: Copy,
{
Self([a; N])
}
}

pub type u8x2 = Simd<u8, 2>;
Expand Down Expand Up @@ -109,6 +103,14 @@ pub type i64x8 = Simd<i64, 8>;
pub type i128x2 = Simd<i128, 2>;
pub type i128x4 = Simd<i128, 4>;

pub type usizex2 = Simd<usize, 2>;
pub type usizex4 = Simd<usize, 4>;
pub type usizex8 = Simd<usize, 8>;

pub type isizex2 = Simd<isize, 2>;
pub type isizex4 = Simd<isize, 4>;
pub type isizex8 = Simd<isize, 8>;

pub type f32x2 = Simd<f32, 2>;
pub type f32x4 = Simd<f32, 4>;
pub type f32x8 = Simd<f32, 8>;
Expand All @@ -122,7 +124,7 @@ pub type f64x8 = Simd<f64, 8>;
// which attempt to just construct it directly. That still works; it's just the
// `.0` projection that doesn't.
#[repr(simd, packed)]
#[derive(Copy)]
#[derive(Copy, Eq)]
pub struct PackedSimd<T, const N: usize>(pub [T; N]);

impl<T: Copy, const N: usize> Clone for PackedSimd<T, N> {
Expand All @@ -131,12 +133,6 @@ impl<T: Copy, const N: usize> Clone for PackedSimd<T, N> {
}
}

impl<T: PartialEq, const N: usize> PartialEq for PackedSimd<T, N> {
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}

impl<T: core::fmt::Debug, const N: usize> core::fmt::Debug for PackedSimd<T, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
<[T; N] as core::fmt::Debug>::fmt(self.as_array(), f)
Expand All @@ -147,14 +143,100 @@ impl<T, const N: usize> PackedSimd<T, N> {
pub const fn from_array(a: [T; N]) -> Self {
PackedSimd(a)
}
pub fn as_array(&self) -> &[T; N] {
pub const fn as_array(&self) -> &[T; N] {
let p: *const Self = self;
unsafe { &*p.cast::<[T; N]>() }
}
pub fn into_array(self) -> [T; N]
pub const fn into_array(self) -> [T; N]
where
T: Copy,
{
*self.as_array()
}
pub const fn splat(a: T) -> Self
where
T: Copy,
{
Self([a; N])
}
}

// As `const_trait_impl` is a language feature with specialized syntax, we have to use them in a way
// such that it doesn't get parsed as Rust code unless `cfg(minisimd_const)` is on. The easiest way
// for that is a macro

#[cfg(minisimd_const)]
macro_rules! impl_traits {
() => {
impl<T: [const] PartialEq, const N: usize> const PartialEq for Simd<T, N> {
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}

impl<T, const N: usize> const core::ops::Index<usize> for Simd<T, N> {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_array()[i]
}
}

impl<T: [const] PartialEq, const N: usize> const PartialEq for PackedSimd<T, N> {
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}
};
}

#[cfg(not(minisimd_const))]
macro_rules! impl_traits {
() => {
impl<T: PartialEq, const N: usize> PartialEq for Simd<T, N> {
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}

impl<T, const N: usize> core::ops::Index<usize> for Simd<T, N> {
type Output = T;
fn index(&self, i: usize) -> &T {
&self.as_array()[i]
}
}

impl<T: PartialEq, const N: usize> PartialEq for PackedSimd<T, N> {
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}
};
}

impl_traits!();

/// Version of `assert_eq` that ignores fancy runtime printing in const context
#[cfg(minisimd_const)]
#[macro_export]
macro_rules! assert_eq_const_safe {
($left:expr, $right:expr $(,)?) => {
assert_eq_const_safe!(
$left,
$right,
concat!("`", stringify!($left), "` == `", stringify!($right), "`")
);
};
($left:expr, $right:expr$(, $($arg:tt)+)?) => {
{
let left = $left;
let right = $right;
// type inference works better with the concrete type on the
// left, but humans work better with the expected on the
// right
assert!(right == left, $($($arg)*),*);
}
};
}

#[cfg(minisimd_const)]
use assert_eq_const_safe;
54 changes: 35 additions & 19 deletions tests/ui/simd/intrinsic/float-math-pass.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//@ run-pass
//@ ignore-emscripten
//@ ignore-android
//@ compile-flags: --cfg minisimd_const

// FIXME: this test fails on arm-android because the NDK version 14 is too old.
// It needs at least version 18. We disable it on all android build bots because
// there is no way in compile-test to disable it for an (arch,os) pair.

// Test that the simd floating-point math intrinsics produce correct results.

#![feature(repr_simd, intrinsics, core_intrinsics)]
#![feature(repr_simd, core_intrinsics, const_trait_impl, const_cmp, const_index)]
#![allow(non_camel_case_types)]

#[path = "../../../auxiliary/minisimd.rs"]
Expand All @@ -34,7 +35,7 @@ macro_rules! assert_approx_eq {
}};
}

fn main() {
const fn abs_and_rounding() {
let x = f32x4::from_array([1.0, 1.0, 1.0, 1.0]);
let y = f32x4::from_array([-1.0, -1.0, -1.0, -1.0]);
let z = f32x4::from_array([0.0, 0.0, 0.0, 0.0]);
Expand All @@ -43,8 +44,33 @@ fn main() {

unsafe {
let r = simd_fabs(y);
assert_approx_eq!(x, r);
assert_eq_const_safe!(x, r);

// rounding functions
let r = simd_floor(h);
assert_eq_const_safe!(z, r);

let r = simd_ceil(h);
assert_eq_const_safe!(x, r);

let r = simd_round(h);
assert_eq_const_safe!(x, r);

let r = simd_round_ties_even(h);
assert_eq_const_safe!(z, r);

let r = simd_trunc(h);
assert_eq_const_safe!(z, r);
}
}

fn math_functions() {
let x = f32x4::from_array([1.0, 1.0, 1.0, 1.0]);
let z = f32x4::from_array([0.0, 0.0, 0.0, 0.0]);

let h = f32x4::from_array([0.5, 0.5, 0.5, 0.5]);

unsafe {
let r = simd_fcos(z);
assert_approx_eq!(x, r);

Expand Down Expand Up @@ -74,21 +100,11 @@ fn main() {

let r = simd_fsin(z);
assert_approx_eq!(z, r);

// rounding functions
let r = simd_floor(h);
assert_eq!(z, r);

let r = simd_ceil(h);
assert_eq!(x, r);

let r = simd_round(h);
assert_eq!(x, r);

let r = simd_round_ties_even(h);
assert_eq!(z, r);

let r = simd_trunc(h);
assert_eq!(z, r);
}
}

fn main() {
const { abs_and_rounding() };
abs_and_rounding();
math_functions();
}
26 changes: 16 additions & 10 deletions tests/ui/simd/intrinsic/float-minmax-pass.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//@ run-pass
//@ ignore-emscripten
//@ compile-flags: --cfg minisimd_const

// Test that the simd_f{min,max} intrinsics produce the correct results.

#![feature(repr_simd, core_intrinsics)]
#![feature(repr_simd, core_intrinsics, const_trait_impl, const_cmp, const_index)]
#![allow(non_camel_case_types)]

#[path = "../../../auxiliary/minisimd.rs"]
Expand All @@ -12,7 +13,7 @@ use minisimd::*;

use std::intrinsics::simd::*;

fn main() {
const fn minmax() {
let x = f32x4::from_array([1.0, 2.0, 3.0, 4.0]);
let y = f32x4::from_array([2.0, 1.0, 4.0, 3.0]);

Expand All @@ -28,22 +29,27 @@ fn main() {
unsafe {
let min0 = simd_fmin(x, y);
let min1 = simd_fmin(y, x);
assert_eq!(min0, min1);
assert_eq_const_safe!(min0, min1);
let e = f32x4::from_array([1.0, 1.0, 3.0, 3.0]);
assert_eq!(min0, e);
assert_eq_const_safe!(min0, e);
let minn = simd_fmin(x, n);
assert_eq!(minn, x);
assert_eq_const_safe!(minn, x);
let minn = simd_fmin(y, n);
assert_eq!(minn, y);
assert_eq_const_safe!(minn, y);

let max0 = simd_fmax(x, y);
let max1 = simd_fmax(y, x);
assert_eq!(max0, max1);
assert_eq_const_safe!(max0, max1);
let e = f32x4::from_array([2.0, 2.0, 4.0, 4.0]);
assert_eq!(max0, e);
assert_eq_const_safe!(max0, e);
let maxn = simd_fmax(x, n);
assert_eq!(maxn, x);
assert_eq_const_safe!(maxn, x);
let maxn = simd_fmax(y, n);
assert_eq!(maxn, y);
assert_eq_const_safe!(maxn, y);
}
}

fn main() {
const { minmax() };
minmax();
}
Loading
Loading