Skip to content

Commit 3d8721b

Browse files
committed
Fix casts, add tests
1 parent 75fdacd commit 3d8721b

File tree

6 files changed

+149
-25
lines changed

6 files changed

+149
-25
lines changed

crates/core_simd/src/intrinsics.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,7 @@ extern "platform-intrinsic" {
3636

3737
/// xor
3838
pub(crate) fn simd_xor<T>(x: T, y: T) -> T;
39+
40+
/// fptoui/fptosi/uitofp/sitofp
41+
pub(crate) fn simd_cast<T, U>(x: T) -> U;
3942
}

crates/core_simd/src/round.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ macro_rules! implement {
66
ceil = $ceil_intrinsic:literal,
77
round = $round_intrinsic:literal,
88
trunc = $trunc_intrinsic:literal,
9-
round_to_int = $round_to_int_intrinsic:literal,
109
}
1110
} => {
1211
mod $type {
@@ -20,8 +19,6 @@ macro_rules! implement {
2019
fn round_intrinsic(x: crate::$type) -> crate::$type;
2120
#[link_name = $trunc_intrinsic]
2221
fn trunc_intrinsic(x: crate::$type) -> crate::$type;
23-
#[link_name = $round_to_int_intrinsic]
24-
fn round_to_int_intrinsic(x: crate::$type) -> crate::$int_type;
2522
}
2623

2724
impl crate::$type {
@@ -60,11 +57,24 @@ macro_rules! implement {
6057
self - self.trunc()
6158
}
6259

60+
/// Rounds toward zero and converts to the same-width integer type, assuming that
61+
/// the value is finite and fits in that type.
62+
///
63+
/// # Safety
64+
/// The value must:
65+
///
66+
/// * Not be NaN
67+
/// * Not be infinite
68+
/// * Be representable in the return type, after truncating off its fractional part
69+
#[inline]
70+
pub unsafe fn to_int_unchecked(self) -> crate::$int_type {
71+
crate::intrinsics::simd_cast(self)
72+
}
73+
6374
/// Returns the nearest integer to each lane. Round half-way cases away from 0.0.
64-
#[must_use = "method returns a new vector and does not mutate the original value"]
6575
#[inline]
66-
pub fn round_to_int(self) -> crate::$int_type {
67-
unsafe { round_to_int_intrinsic(self) }
76+
pub fn round_from_int(value: crate::$int_type) -> Self {
77+
unsafe { crate::intrinsics::simd_cast(value) }
6878
}
6979
}
7080
}
@@ -78,7 +88,6 @@ implement! {
7888
ceil = "llvm.ceil.v2f32",
7989
round = "llvm.round.v2f32",
8090
trunc = "llvm.trunc.v2f32",
81-
round_to_int = "llvm.lround.i32.v2f32",
8291
}
8392
}
8493

@@ -89,7 +98,6 @@ implement! {
8998
ceil = "llvm.ceil.v4f32",
9099
round = "llvm.round.v4f32",
91100
trunc = "llvm.trunc.v4f32",
92-
round_to_int = "llvm.lround.i32.v4f32",
93101
}
94102
}
95103

@@ -100,7 +108,6 @@ implement! {
100108
ceil = "llvm.ceil.v8f32",
101109
round = "llvm.round.v8f32",
102110
trunc = "llvm.trunc.v8f32",
103-
round_to_int = "llvm.lround.i32.v8f32",
104111
}
105112
}
106113

@@ -111,7 +118,6 @@ implement! {
111118
ceil = "llvm.ceil.v16f32",
112119
round = "llvm.round.v16f32",
113120
trunc = "llvm.trunc.v16f32",
114-
round_to_int = "llvm.lround.i32.v16f32",
115121
}
116122
}
117123

@@ -122,7 +128,6 @@ implement! {
122128
ceil = "llvm.ceil.v2f64",
123129
round = "llvm.round.v2f64",
124130
trunc = "llvm.trunc.v2f64",
125-
round_to_int = "llvm.lround.i64.v2f64",
126131
}
127132
}
128133

@@ -133,7 +138,6 @@ implement! {
133138
ceil = "llvm.ceil.v4f64",
134139
round = "llvm.round.v4f64",
135140
trunc = "llvm.trunc.v4f64",
136-
round_to_int = "llvm.lround.i64.v4f64",
137141
}
138142
}
139143

@@ -144,6 +148,5 @@ implement! {
144148
ceil = "llvm.ceil.v8f64",
145149
round = "llvm.round.v8f64",
146150
trunc = "llvm.trunc.v8f64",
147-
round_to_int = "llvm.lround.i64.v8f64",
148151
}
149152
}

crates/core_simd/tests/helpers/lanewise.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
pub fn apply_unary_lanewise<T: Copy, V: AsMut<[T]> + Default>(mut x: V, f: impl Fn(T) -> T) -> V {
2-
for lane in x.as_mut() {
3-
*lane = f(*lane)
1+
pub fn apply_unary_lanewise<T1: Copy, T2: Copy, V1: AsRef<[T1]>, V2: AsMut<[T2]> + Default>(
2+
x: V1,
3+
f: impl Fn(T1) -> T2,
4+
) -> V2 {
5+
let mut y = V2::default();
6+
assert_eq!(x.as_ref().len(), y.as_mut().len());
7+
for (x, y) in x.as_ref().iter().zip(y.as_mut().iter_mut()) {
8+
*y = f(*x);
49
}
5-
x
10+
y
611
}
712

813
pub fn apply_binary_lanewise<T: Copy, V: AsRef<[T]> + AsMut<[T]> + Default>(
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::helpers;
22

3-
float_tests! { f32x2, f32 }
4-
float_tests! { f32x4, f32 }
5-
float_tests! { f32x8, f32 }
6-
float_tests! { f32x16, f32 }
3+
float_tests! { f32x2, f32, i32x2, i32 }
4+
float_tests! { f32x4, f32, i32x4, i32 }
5+
float_tests! { f32x8, f32, i32x8, i32 }
6+
float_tests! { f32x16, f32, i32x16, i32 }
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::helpers;
22

3-
float_tests! { f64x2, f64 }
4-
float_tests! { f64x4, f64 }
5-
float_tests! { f64x8, f64 }
3+
float_tests! { f64x2, f64, i64x2, i64 }
4+
float_tests! { f64x4, f64, i64x4, i64 }
5+
float_tests! { f64x8, f64, i64x8, i64 }

crates/core_simd/tests/ops_impl/float_macros.rs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
macro_rules! float_tests {
2-
{ $vector:ident, $scalar:ident } => {
2+
{ $vector:ident, $scalar:ident, $int_vector:ident, $int_scalar:ident } => {
33
#[cfg(test)]
44
mod $vector {
55
use super::*;
@@ -24,6 +24,18 @@ macro_rules! float_tests {
2424
slice.chunks_exact(lanes).map(from_slice)
2525
}
2626

27+
fn from_slice_int(slice: &[$int_scalar]) -> core_simd::$int_vector {
28+
let mut value = core_simd::$int_vector::default();
29+
let value_slice: &mut [_] = value.as_mut();
30+
value_slice.copy_from_slice(&slice[0..value_slice.len()]);
31+
value
32+
}
33+
34+
fn slice_chunks_int(slice: &[$int_scalar]) -> impl Iterator<Item = core_simd::$int_vector> + '_ {
35+
let lanes = core::mem::size_of::<core_simd::$int_vector>() / core::mem::size_of::<$int_scalar>();
36+
slice.chunks_exact(lanes).map(from_slice_int)
37+
}
38+
2739
const A: [$scalar; 16] = [0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15.];
2840
const B: [$scalar; 16] = [16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31.];
2941
const C: [$scalar; 16] = [
@@ -322,6 +334,107 @@ macro_rules! float_tests {
322334
assert_biteq!(v.abs(), expected);
323335
}
324336
}
337+
338+
#[test]
339+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
340+
fn ceil_odd_floats() {
341+
for v in slice_chunks(&C) {
342+
let expected = apply_unary_lanewise(v, <$scalar>::ceil);
343+
assert_biteq!(v.ceil(), expected);
344+
}
345+
}
346+
347+
#[test]
348+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
349+
fn floor_odd_floats() {
350+
for v in slice_chunks(&C) {
351+
let expected = apply_unary_lanewise(v, <$scalar>::floor);
352+
assert_biteq!(v.floor(), expected);
353+
}
354+
}
355+
356+
#[test]
357+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
358+
fn round_odd_floats() {
359+
for v in slice_chunks(&C) {
360+
let expected = apply_unary_lanewise(v, <$scalar>::round);
361+
assert_biteq!(v.round(), expected);
362+
}
363+
}
364+
365+
#[test]
366+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
367+
fn trunc_odd_floats() {
368+
for v in slice_chunks(&C) {
369+
let expected = apply_unary_lanewise(v, <$scalar>::trunc);
370+
assert_biteq!(v.trunc(), expected);
371+
}
372+
}
373+
374+
#[test]
375+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
376+
fn fract_odd_floats() {
377+
for v in slice_chunks(&C) {
378+
let expected = apply_unary_lanewise(v, <$scalar>::fract);
379+
assert_biteq!(v.fract(), expected);
380+
}
381+
}
382+
383+
#[test]
384+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
385+
fn to_int_unchecked() {
386+
const VALUES: [$scalar; 16] = [
387+
-0.0,
388+
0.0,
389+
-1.0,
390+
1.0,
391+
<$scalar>::MIN,
392+
-<$scalar>::MIN,
393+
<$scalar>::MIN_POSITIVE,
394+
-<$scalar>::MIN_POSITIVE,
395+
<$scalar>::EPSILON,
396+
-<$scalar>::EPSILON,
397+
core::$scalar::consts::PI,
398+
-core::$scalar::consts::PI,
399+
core::$scalar::consts::TAU,
400+
-core::$scalar::consts::TAU,
401+
100.0 / 3.0,
402+
-100.0 / 3.0,
403+
];
404+
405+
for v in slice_chunks(&VALUES) {
406+
let expected = apply_unary_lanewise(v, |x| unsafe { x.to_int_unchecked() });
407+
assert_biteq!(unsafe { v.to_int_unchecked() }, expected);
408+
}
409+
}
410+
411+
#[test]
412+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
413+
fn round_from_int() {
414+
const VALUES: [$int_scalar; 16] = [
415+
0,
416+
0,
417+
1,
418+
-1,
419+
100,
420+
-100,
421+
200,
422+
-200,
423+
413,
424+
-413,
425+
1017,
426+
-1017,
427+
1234567,
428+
-1234567,
429+
<$int_scalar>::MAX,
430+
<$int_scalar>::MIN,
431+
];
432+
433+
for v in slice_chunks_int(&VALUES) {
434+
let expected = apply_unary_lanewise(v, |x| x as $scalar);
435+
assert_biteq!(core_simd::$vector::round_from_int(v), expected);
436+
}
437+
}
325438
}
326439
}
327440
}

0 commit comments

Comments
 (0)