Skip to content

Commit e48c21b

Browse files
committed
Enable const-testing for the ported SIMD intrinsics
1 parent 740b8ba commit e48c21b

18 files changed

+429
-278
lines changed

tests/auxiliary/minisimd.rs

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
1111
#![allow(unused)]
1212
#![allow(non_camel_case_types)]
13+
// FIXME: `cfg(minisimd_const)` is used to toggle use of const trait impls, which require a few
14+
// nightly features. Remove this when `const_trait_impls`, `const_cmp` and `const_index` are
15+
// stablilized.
16+
#![allow(unexpected_cfgs)]
1317

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

27-
impl<T: PartialEq, const N: usize> PartialEq for Simd<T, N> {
28-
fn eq(&self, other: &Self) -> bool {
29-
self.as_array() == other.as_array()
30-
}
31-
}
32-
3331
impl<T: core::fmt::Debug, const N: usize> core::fmt::Debug for Simd<T, N> {
3432
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
3533
<[T; N] as core::fmt::Debug>::fmt(self.as_array(), f)
3634
}
3735
}
3836

39-
impl<T, const N: usize> core::ops::Index<usize> for Simd<T, N> {
40-
type Output = T;
41-
fn index(&self, i: usize) -> &T {
42-
&self.as_array()[i]
43-
}
44-
}
45-
4637
impl<T, const N: usize> Simd<T, N> {
4738
pub const fn from_array(a: [T; N]) -> Self {
4839
Simd(a)
4940
}
50-
pub fn as_array(&self) -> &[T; N] {
41+
pub const fn as_array(&self) -> &[T; N] {
5142
let p: *const Self = self;
5243
unsafe { &*p.cast::<[T; N]>() }
5344
}
54-
pub fn into_array(self) -> [T; N]
45+
pub const fn into_array(self) -> [T; N]
5546
where
5647
T: Copy,
5748
{
5849
*self.as_array()
5950
}
51+
pub const fn splat(a: T) -> Self
52+
where
53+
T: Copy,
54+
{
55+
Self([a; N])
56+
}
6057
}
6158

6259
pub type u8x2 = Simd<u8, 2>;
@@ -109,6 +106,14 @@ pub type i64x8 = Simd<i64, 8>;
109106
pub type i128x2 = Simd<i128, 2>;
110107
pub type i128x4 = Simd<i128, 4>;
111108

109+
pub type usizex2 = Simd<usize, 2>;
110+
pub type usizex4 = Simd<usize, 4>;
111+
pub type usizex8 = Simd<usize, 8>;
112+
113+
pub type isizex2 = Simd<isize, 2>;
114+
pub type isizex4 = Simd<isize, 4>;
115+
pub type isizex8 = Simd<isize, 8>;
116+
112117
pub type f32x2 = Simd<f32, 2>;
113118
pub type f32x4 = Simd<f32, 4>;
114119
pub type f32x8 = Simd<f32, 8>;
@@ -122,7 +127,7 @@ pub type f64x8 = Simd<f64, 8>;
122127
// which attempt to just construct it directly. That still works; it's just the
123128
// `.0` projection that doesn't.
124129
#[repr(simd, packed)]
125-
#[derive(Copy)]
130+
#[derive(Copy, Eq)]
126131
pub struct PackedSimd<T, const N: usize>(pub [T; N]);
127132

128133
impl<T: Copy, const N: usize> Clone for PackedSimd<T, N> {
@@ -131,12 +136,6 @@ impl<T: Copy, const N: usize> Clone for PackedSimd<T, N> {
131136
}
132137
}
133138

134-
impl<T: PartialEq, const N: usize> PartialEq for PackedSimd<T, N> {
135-
fn eq(&self, other: &Self) -> bool {
136-
self.as_array() == other.as_array()
137-
}
138-
}
139-
140139
impl<T: core::fmt::Debug, const N: usize> core::fmt::Debug for PackedSimd<T, N> {
141140
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
142141
<[T; N] as core::fmt::Debug>::fmt(self.as_array(), f)
@@ -147,14 +146,80 @@ impl<T, const N: usize> PackedSimd<T, N> {
147146
pub const fn from_array(a: [T; N]) -> Self {
148147
PackedSimd(a)
149148
}
150-
pub fn as_array(&self) -> &[T; N] {
149+
pub const fn as_array(&self) -> &[T; N] {
151150
let p: *const Self = self;
152151
unsafe { &*p.cast::<[T; N]>() }
153152
}
154-
pub fn into_array(self) -> [T; N]
153+
pub const fn into_array(self) -> [T; N]
155154
where
156155
T: Copy,
157156
{
158157
*self.as_array()
159158
}
159+
pub const fn splat(a: T) -> Self
160+
where
161+
T: Copy,
162+
{
163+
Self([a; N])
164+
}
165+
}
166+
167+
// As `const_trait_impl` is a language feature with specialized syntax, we have to use them in a way
168+
// such that it doesn't get parsed as Rust code unless `cfg(minisimd_const)` is on. The easiest way
169+
// for that is a macro
170+
171+
macro_rules! impl_traits {
172+
($($const_:ident)?) => {
173+
impl<T: $([$const_])? PartialEq, const N: usize> $($const_)? PartialEq for Simd<T, N> {
174+
fn eq(&self, other: &Self) -> bool {
175+
self.as_array() == other.as_array()
176+
}
177+
}
178+
179+
impl<T, const N: usize> $($const_)? core::ops::Index<usize> for Simd<T, N> {
180+
type Output = T;
181+
fn index(&self, i: usize) -> &T {
182+
&self.as_array()[i]
183+
}
184+
}
185+
186+
impl<T: $([$const_])? PartialEq, const N: usize> $($const_)? PartialEq for PackedSimd<T, N>
187+
{
188+
fn eq(&self, other: &Self) -> bool {
189+
self.as_array() == other.as_array()
190+
}
191+
}
192+
};
160193
}
194+
195+
#[cfg(minisimd_const)]
196+
impl_traits!(const);
197+
198+
#[cfg(not(minisimd_const))]
199+
impl_traits!();
200+
201+
/// Version of `assert_eq` that ignores fancy runtime printing in const context
202+
#[cfg(minisimd_const)]
203+
#[macro_export]
204+
macro_rules! assert_eq {
205+
($left:expr, $right:expr $(,)?) => {
206+
assert_eq!(
207+
$left,
208+
$right,
209+
concat!("`", stringify!($left), "` == `", stringify!($right), "`")
210+
);
211+
};
212+
($left:expr, $right:expr$(, $($arg:tt)+)?) => {
213+
{
214+
let left = $left;
215+
let right = $right;
216+
// type inference works better with the concrete type on the
217+
// left, but humans work better with the expected on the
218+
// right
219+
assert!(right == left, $($($arg)*),*);
220+
}
221+
};
222+
}
223+
224+
#[cfg(minisimd_const)]
225+
use assert_eq;
Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
//@ run-pass
22
//@ ignore-emscripten
33
//@ ignore-android
4+
//@ compile-flags: --cfg minisimd_const
45

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

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

11-
#![feature(repr_simd, intrinsics, core_intrinsics)]
12+
#![feature(repr_simd, core_intrinsics, const_trait_impl, const_cmp, const_index)]
1213
#![allow(non_camel_case_types)]
1314

1415
#[path = "../../../auxiliary/minisimd.rs"]
@@ -20,7 +21,10 @@ use std::intrinsics::simd::*;
2021
macro_rules! assert_approx_eq_f32 {
2122
($a:expr, $b:expr) => {{
2223
let (a, b) = (&$a, &$b);
23-
assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
24+
assert!(
25+
(*a - *b).abs() < 1.0e-6,
26+
concat!(stringify!($a), " is not approximately equal to ", stringify!($b))
27+
);
2428
}};
2529
}
2630
macro_rules! assert_approx_eq {
@@ -34,7 +38,7 @@ macro_rules! assert_approx_eq {
3438
}};
3539
}
3640

37-
fn main() {
41+
const fn simple_math() {
3842
let x = f32x4::from_array([1.0, 1.0, 1.0, 1.0]);
3943
let y = f32x4::from_array([-1.0, -1.0, -1.0, -1.0]);
4044
let z = f32x4::from_array([0.0, 0.0, 0.0, 0.0]);
@@ -43,21 +47,44 @@ fn main() {
4347

4448
unsafe {
4549
let r = simd_fabs(y);
46-
assert_approx_eq!(x, r);
50+
assert_eq!(x, r);
4751

48-
let r = simd_fcos(z);
52+
// rounding functions
53+
let r = simd_floor(h);
54+
assert_eq!(z, r);
55+
56+
let r = simd_ceil(h);
57+
assert_eq!(x, r);
58+
59+
let r = simd_round(h);
60+
assert_eq!(x, r);
61+
62+
let r = simd_round_ties_even(h);
63+
assert_eq!(z, r);
64+
65+
let r = simd_trunc(h);
66+
assert_eq!(z, r);
67+
68+
let r = simd_fma(x, h, h);
4969
assert_approx_eq!(x, r);
5070

51-
let r = simd_fexp(z);
71+
let r = simd_relaxed_fma(x, h, h);
5272
assert_approx_eq!(x, r);
73+
}
74+
}
5375

54-
let r = simd_fexp2(z);
76+
fn special_math() {
77+
let x = f32x4::from_array([1.0, 1.0, 1.0, 1.0]);
78+
let z = f32x4::from_array([0.0, 0.0, 0.0, 0.0]);
79+
80+
unsafe {
81+
let r = simd_fcos(z);
5582
assert_approx_eq!(x, r);
5683

57-
let r = simd_fma(x, h, h);
84+
let r = simd_fexp(z);
5885
assert_approx_eq!(x, r);
5986

60-
let r = simd_relaxed_fma(x, h, h);
87+
let r = simd_fexp2(z);
6188
assert_approx_eq!(x, r);
6289

6390
let r = simd_fsqrt(x);
@@ -74,21 +101,11 @@ fn main() {
74101

75102
let r = simd_fsin(z);
76103
assert_approx_eq!(z, r);
77-
78-
// rounding functions
79-
let r = simd_floor(h);
80-
assert_eq!(z, r);
81-
82-
let r = simd_ceil(h);
83-
assert_eq!(x, r);
84-
85-
let r = simd_round(h);
86-
assert_eq!(x, r);
87-
88-
let r = simd_round_ties_even(h);
89-
assert_eq!(z, r);
90-
91-
let r = simd_trunc(h);
92-
assert_eq!(z, r);
93104
}
94105
}
106+
107+
fn main() {
108+
const { simple_math() };
109+
simple_math();
110+
special_math();
111+
}

tests/ui/simd/intrinsic/float-minmax-pass.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//@ run-pass
22
//@ ignore-emscripten
3+
//@ compile-flags: --cfg minisimd_const
34

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

6-
#![feature(repr_simd, core_intrinsics)]
7+
#![feature(repr_simd, core_intrinsics, const_trait_impl, const_cmp, const_index)]
78
#![allow(non_camel_case_types)]
89

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

1314
use std::intrinsics::simd::*;
1415

15-
fn main() {
16+
const fn minmax() {
1617
let x = f32x4::from_array([1.0, 2.0, 3.0, 4.0]);
1718
let y = f32x4::from_array([2.0, 1.0, 4.0, 3.0]);
1819

@@ -47,3 +48,8 @@ fn main() {
4748
assert_eq!(maxn, y);
4849
}
4950
}
51+
52+
fn main() {
53+
const { minmax() };
54+
minmax();
55+
}

tests/ui/simd/intrinsic/generic-arithmetic-pass.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//@ run-pass
22
//@ ignore-backends: gcc
3+
//@ compile-flags: --cfg minisimd_const
34

45
#![allow(non_camel_case_types)]
5-
#![feature(repr_simd, core_intrinsics)]
6+
#![feature(repr_simd, core_intrinsics, const_trait_impl, const_cmp, const_index)]
67

78
#[path = "../../../auxiliary/minisimd.rs"]
89
mod minisimd;
@@ -20,7 +21,7 @@ macro_rules! all_eq {
2021

2122
use std::intrinsics::simd::*;
2223

23-
fn main() {
24+
const fn arithmetic() {
2425
let x1 = i32x4::from_array([1, 2, 3, 4]);
2526
let y1 = U32::<4>::from_array([1, 2, 3, 4]);
2627
let z1 = f32x4::from_array([1.0, 2.0, 3.0, 4.0]);
@@ -224,3 +225,8 @@ fn main() {
224225
all_eq!(simd_cttz(y1), U32::<4>::from_array([0, 1, 0, 2]));
225226
}
226227
}
228+
229+
fn main() {
230+
const { arithmetic() };
231+
arithmetic();
232+
}

tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//@ run-pass
22
//@ ignore-emscripten
3+
//@ compile-flags: --cfg minisimd_const
34

45
#![allow(non_camel_case_types)]
5-
#![feature(repr_simd, core_intrinsics)]
6+
#![feature(repr_simd, core_intrinsics, const_trait_impl, const_cmp, const_index)]
67

78
#[path = "../../../auxiliary/minisimd.rs"]
89
mod minisimd;
@@ -12,7 +13,7 @@ use std::intrinsics::simd::{simd_saturating_add, simd_saturating_sub};
1213

1314
type I32<const N: usize> = Simd<i32, N>;
1415

15-
fn main() {
16+
const fn saturating() {
1617
// unsigned
1718
{
1819
const M: u32 = u32::MAX;
@@ -84,3 +85,8 @@ fn main() {
8485
}
8586
}
8687
}
88+
89+
fn main() {
90+
const { saturating() };
91+
saturating();
92+
}

0 commit comments

Comments
 (0)