Skip to content

Commit 87779ae

Browse files
authoredOct 29, 2022
Merge pull request #287 from rust-lang/feature/pointer-vectors
Vectors of pointers
2 parents 7c80b69 + 469c620 commit 87779ae

File tree

14 files changed

+658
-89
lines changed

14 files changed

+658
-89
lines changed
 

‎crates/core_simd/src/cast.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use crate::simd::SimdElement;
2+
3+
/// Supporting trait for `Simd::cast`. Typically doesn't need to be used directly.
4+
///
5+
/// # Safety
6+
/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast` or
7+
/// `simd_as` intrinsics.
8+
pub unsafe trait SimdCast: SimdElement {}
9+
10+
// Safety: primitive number types can be cast to other primitive number types
11+
unsafe impl SimdCast for i8 {}
12+
// Safety: primitive number types can be cast to other primitive number types
13+
unsafe impl SimdCast for i16 {}
14+
// Safety: primitive number types can be cast to other primitive number types
15+
unsafe impl SimdCast for i32 {}
16+
// Safety: primitive number types can be cast to other primitive number types
17+
unsafe impl SimdCast for i64 {}
18+
// Safety: primitive number types can be cast to other primitive number types
19+
unsafe impl SimdCast for isize {}
20+
// Safety: primitive number types can be cast to other primitive number types
21+
unsafe impl SimdCast for u8 {}
22+
// Safety: primitive number types can be cast to other primitive number types
23+
unsafe impl SimdCast for u16 {}
24+
// Safety: primitive number types can be cast to other primitive number types
25+
unsafe impl SimdCast for u32 {}
26+
// Safety: primitive number types can be cast to other primitive number types
27+
unsafe impl SimdCast for u64 {}
28+
// Safety: primitive number types can be cast to other primitive number types
29+
unsafe impl SimdCast for usize {}
30+
// Safety: primitive number types can be cast to other primitive number types
31+
unsafe impl SimdCast for f32 {}
32+
// Safety: primitive number types can be cast to other primitive number types
33+
unsafe impl SimdCast for f64 {}
34+
35+
/// Supporting trait for `Simd::cast_ptr`. Typically doesn't need to be used directly.
36+
///
37+
/// # Safety
38+
/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast_ptr`
39+
/// intrinsic.
40+
pub unsafe trait SimdCastPtr<T> {}
41+
42+
// Safety: pointers can be cast to other pointer types
43+
unsafe impl<T, U> SimdCastPtr<T> for *const U
44+
where
45+
U: core::ptr::Pointee,
46+
T: core::ptr::Pointee<Metadata = U::Metadata>,
47+
{
48+
}
49+
// Safety: pointers can be cast to other pointer types
50+
unsafe impl<T, U> SimdCastPtr<T> for *mut U
51+
where
52+
U: core::ptr::Pointee,
53+
T: core::ptr::Pointee<Metadata = U::Metadata>,
54+
{
55+
}

‎crates/core_simd/src/elements.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
mod const_ptr;
12
mod float;
23
mod int;
4+
mod mut_ptr;
35
mod uint;
46

57
mod sealed {
68
pub trait Sealed {}
79
}
810

11+
pub use const_ptr::*;
912
pub use float::*;
1013
pub use int::*;
14+
pub use mut_ptr::*;
1115
pub use uint::*;
+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use super::sealed::Sealed;
2+
use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount};
3+
4+
/// Operations on SIMD vectors of constant pointers.
5+
pub trait SimdConstPtr: Copy + Sealed {
6+
/// Vector of `usize` with the same number of lanes.
7+
type Usize;
8+
9+
/// Vector of `isize` with the same number of lanes.
10+
type Isize;
11+
12+
/// Vector of mutable pointers to the same type.
13+
type MutPtr;
14+
15+
/// Mask type used for manipulating this SIMD vector type.
16+
type Mask;
17+
18+
/// Returns `true` for each lane that is null.
19+
fn is_null(self) -> Self::Mask;
20+
21+
/// Changes constness without changing the type.
22+
fn as_mut(self) -> Self::MutPtr;
23+
24+
/// Gets the "address" portion of the pointer.
25+
///
26+
/// This method discards pointer semantic metadata, so the result cannot be
27+
/// directly cast into a valid pointer.
28+
///
29+
/// This method semantically discards *provenance* and
30+
/// *address-space* information. To properly restore that information, use [`Self::with_addr`].
31+
///
32+
/// Equivalent to calling [`pointer::addr`] on each lane.
33+
fn addr(self) -> Self::Usize;
34+
35+
/// Creates a new pointer with the given address.
36+
///
37+
/// This performs the same operation as a cast, but copies the *address-space* and
38+
/// *provenance* of `self` to the new pointer.
39+
///
40+
/// Equivalent to calling [`pointer::with_addr`] on each lane.
41+
fn with_addr(self, addr: Self::Usize) -> Self;
42+
43+
/// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use
44+
/// in [`Self::from_exposed_addr`].
45+
fn expose_addr(self) -> Self::Usize;
46+
47+
/// Convert an address back to a pointer, picking up a previously "exposed" provenance.
48+
///
49+
/// Equivalent to calling [`core::ptr::from_exposed_addr`] on each lane.
50+
fn from_exposed_addr(addr: Self::Usize) -> Self;
51+
52+
/// Calculates the offset from a pointer using wrapping arithmetic.
53+
///
54+
/// Equivalent to calling [`pointer::wrapping_offset`] on each lane.
55+
fn wrapping_offset(self, offset: Self::Isize) -> Self;
56+
57+
/// Calculates the offset from a pointer using wrapping arithmetic.
58+
///
59+
/// Equivalent to calling [`pointer::wrapping_add`] on each lane.
60+
fn wrapping_add(self, count: Self::Usize) -> Self;
61+
62+
/// Calculates the offset from a pointer using wrapping arithmetic.
63+
///
64+
/// Equivalent to calling [`pointer::wrapping_sub`] on each lane.
65+
fn wrapping_sub(self, count: Self::Usize) -> Self;
66+
}
67+
68+
impl<T, const LANES: usize> Sealed for Simd<*const T, LANES> where
69+
LaneCount<LANES>: SupportedLaneCount
70+
{
71+
}
72+
73+
impl<T, const LANES: usize> SimdConstPtr for Simd<*const T, LANES>
74+
where
75+
LaneCount<LANES>: SupportedLaneCount,
76+
{
77+
type Usize = Simd<usize, LANES>;
78+
type Isize = Simd<isize, LANES>;
79+
type MutPtr = Simd<*mut T, LANES>;
80+
type Mask = Mask<isize, LANES>;
81+
82+
#[inline]
83+
fn is_null(self) -> Self::Mask {
84+
Simd::splat(core::ptr::null()).simd_eq(self)
85+
}
86+
87+
#[inline]
88+
fn as_mut(self) -> Self::MutPtr {
89+
self.cast_ptr()
90+
}
91+
92+
#[inline]
93+
fn addr(self) -> Self::Usize {
94+
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
95+
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
96+
// provenance).
97+
unsafe { core::mem::transmute_copy(&self) }
98+
}
99+
100+
#[inline]
101+
fn with_addr(self, addr: Self::Usize) -> Self {
102+
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
103+
//
104+
// In the mean-time, this operation is defined to be "as if" it was
105+
// a wrapping_offset, so we can emulate it as such. This should properly
106+
// restore pointer provenance even under today's compiler.
107+
self.cast_ptr::<*const u8>()
108+
.wrapping_offset(addr.cast::<isize>() - self.addr().cast::<isize>())
109+
.cast_ptr()
110+
}
111+
112+
#[inline]
113+
fn expose_addr(self) -> Self::Usize {
114+
// Safety: `self` is a pointer vector
115+
unsafe { intrinsics::simd_expose_addr(self) }
116+
}
117+
118+
#[inline]
119+
fn from_exposed_addr(addr: Self::Usize) -> Self {
120+
// Safety: `self` is a pointer vector
121+
unsafe { intrinsics::simd_from_exposed_addr(addr) }
122+
}
123+
124+
#[inline]
125+
fn wrapping_offset(self, count: Self::Isize) -> Self {
126+
// Safety: simd_arith_offset takes a vector of pointers and a vector of offsets
127+
unsafe { intrinsics::simd_arith_offset(self, count) }
128+
}
129+
130+
#[inline]
131+
fn wrapping_add(self, count: Self::Usize) -> Self {
132+
self.wrapping_offset(count.cast())
133+
}
134+
135+
#[inline]
136+
fn wrapping_sub(self, count: Self::Usize) -> Self {
137+
self.wrapping_offset(-count.cast::<isize>())
138+
}
139+
}
+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use super::sealed::Sealed;
2+
use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount};
3+
4+
/// Operations on SIMD vectors of mutable pointers.
5+
pub trait SimdMutPtr: Copy + Sealed {
6+
/// Vector of `usize` with the same number of lanes.
7+
type Usize;
8+
9+
/// Vector of `isize` with the same number of lanes.
10+
type Isize;
11+
12+
/// Vector of constant pointers to the same type.
13+
type ConstPtr;
14+
15+
/// Mask type used for manipulating this SIMD vector type.
16+
type Mask;
17+
18+
/// Returns `true` for each lane that is null.
19+
fn is_null(self) -> Self::Mask;
20+
21+
/// Changes constness without changing the type.
22+
fn as_const(self) -> Self::ConstPtr;
23+
24+
/// Gets the "address" portion of the pointer.
25+
///
26+
/// This method discards pointer semantic metadata, so the result cannot be
27+
/// directly cast into a valid pointer.
28+
///
29+
/// Equivalent to calling [`pointer::addr`] on each lane.
30+
fn addr(self) -> Self::Usize;
31+
32+
/// Creates a new pointer with the given address.
33+
///
34+
/// This performs the same operation as a cast, but copies the *address-space* and
35+
/// *provenance* of `self` to the new pointer.
36+
///
37+
/// Equivalent to calling [`pointer::with_addr`] on each lane.
38+
fn with_addr(self, addr: Self::Usize) -> Self;
39+
40+
/// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use
41+
/// in [`Self::from_exposed_addr`].
42+
fn expose_addr(self) -> Self::Usize;
43+
44+
/// Convert an address back to a pointer, picking up a previously "exposed" provenance.
45+
///
46+
/// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each lane.
47+
fn from_exposed_addr(addr: Self::Usize) -> Self;
48+
49+
/// Calculates the offset from a pointer using wrapping arithmetic.
50+
///
51+
/// Equivalent to calling [`pointer::wrapping_offset`] on each lane.
52+
fn wrapping_offset(self, offset: Self::Isize) -> Self;
53+
54+
/// Calculates the offset from a pointer using wrapping arithmetic.
55+
///
56+
/// Equivalent to calling [`pointer::wrapping_add`] on each lane.
57+
fn wrapping_add(self, count: Self::Usize) -> Self;
58+
59+
/// Calculates the offset from a pointer using wrapping arithmetic.
60+
///
61+
/// Equivalent to calling [`pointer::wrapping_sub`] on each lane.
62+
fn wrapping_sub(self, count: Self::Usize) -> Self;
63+
}
64+
65+
impl<T, const LANES: usize> Sealed for Simd<*mut T, LANES> where LaneCount<LANES>: SupportedLaneCount
66+
{}
67+
68+
impl<T, const LANES: usize> SimdMutPtr for Simd<*mut T, LANES>
69+
where
70+
LaneCount<LANES>: SupportedLaneCount,
71+
{
72+
type Usize = Simd<usize, LANES>;
73+
type Isize = Simd<isize, LANES>;
74+
type ConstPtr = Simd<*const T, LANES>;
75+
type Mask = Mask<isize, LANES>;
76+
77+
#[inline]
78+
fn is_null(self) -> Self::Mask {
79+
Simd::splat(core::ptr::null_mut()).simd_eq(self)
80+
}
81+
82+
#[inline]
83+
fn as_const(self) -> Self::ConstPtr {
84+
self.cast_ptr()
85+
}
86+
87+
#[inline]
88+
fn addr(self) -> Self::Usize {
89+
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
90+
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
91+
// provenance).
92+
unsafe { core::mem::transmute_copy(&self) }
93+
}
94+
95+
#[inline]
96+
fn with_addr(self, addr: Self::Usize) -> Self {
97+
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
98+
//
99+
// In the mean-time, this operation is defined to be "as if" it was
100+
// a wrapping_offset, so we can emulate it as such. This should properly
101+
// restore pointer provenance even under today's compiler.
102+
self.cast_ptr::<*mut u8>()
103+
.wrapping_offset(addr.cast::<isize>() - self.addr().cast::<isize>())
104+
.cast_ptr()
105+
}
106+
107+
#[inline]
108+
fn expose_addr(self) -> Self::Usize {
109+
// Safety: `self` is a pointer vector
110+
unsafe { intrinsics::simd_expose_addr(self) }
111+
}
112+
113+
#[inline]
114+
fn from_exposed_addr(addr: Self::Usize) -> Self {
115+
// Safety: `self` is a pointer vector
116+
unsafe { intrinsics::simd_from_exposed_addr(addr) }
117+
}
118+
119+
#[inline]
120+
fn wrapping_offset(self, count: Self::Isize) -> Self {
121+
// Safety: simd_arith_offset takes a vector of pointers and a vector of offsets
122+
unsafe { intrinsics::simd_arith_offset(self, count) }
123+
}
124+
125+
#[inline]
126+
fn wrapping_add(self, count: Self::Usize) -> Self {
127+
self.wrapping_offset(count.cast())
128+
}
129+
130+
#[inline]
131+
fn wrapping_sub(self, count: Self::Usize) -> Self {
132+
self.wrapping_offset(-count.cast::<isize>())
133+
}
134+
}

‎crates/core_simd/src/eq.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdElement, SupportedLaneCount};
1+
use crate::simd::{
2+
intrinsics, LaneCount, Mask, Simd, SimdConstPtr, SimdElement, SimdMutPtr, SupportedLaneCount,
3+
};
24

35
/// Parallel `PartialEq`.
46
pub trait SimdPartialEq {
@@ -71,3 +73,37 @@ macro_rules! impl_mask {
7173
}
7274

7375
impl_mask! { i8, i16, i32, i64, isize }
76+
77+
impl<T, const LANES: usize> SimdPartialEq for Simd<*const T, LANES>
78+
where
79+
LaneCount<LANES>: SupportedLaneCount,
80+
{
81+
type Mask = Mask<isize, LANES>;
82+
83+
#[inline]
84+
fn simd_eq(self, other: Self) -> Self::Mask {
85+
self.addr().simd_eq(other.addr())
86+
}
87+
88+
#[inline]
89+
fn simd_ne(self, other: Self) -> Self::Mask {
90+
self.addr().simd_ne(other.addr())
91+
}
92+
}
93+
94+
impl<T, const LANES: usize> SimdPartialEq for Simd<*mut T, LANES>
95+
where
96+
LaneCount<LANES>: SupportedLaneCount,
97+
{
98+
type Mask = Mask<isize, LANES>;
99+
100+
#[inline]
101+
fn simd_eq(self, other: Self) -> Self::Mask {
102+
self.addr().simd_eq(other.addr())
103+
}
104+
105+
#[inline]
106+
fn simd_ne(self, other: Self) -> Self::Mask {
107+
self.addr().simd_ne(other.addr())
108+
}
109+
}

‎crates/core_simd/src/intrinsics.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ extern "platform-intrinsic" {
6161
/// xor
6262
pub(crate) fn simd_xor<T>(x: T, y: T) -> T;
6363

64-
/// getelementptr (without inbounds)
65-
pub(crate) fn simd_arith_offset<T, U>(ptrs: T, offsets: U) -> T;
66-
6764
/// fptoui/fptosi/uitofp/sitofp
6865
/// casting floats to integers is truncating, so it is safe to convert values like e.g. 1.5
6966
/// but the truncated value must fit in the target type or the result is poison.
@@ -151,4 +148,17 @@ extern "platform-intrinsic" {
151148
pub(crate) fn simd_select<M, T>(m: M, yes: T, no: T) -> T;
152149
#[allow(unused)]
153150
pub(crate) fn simd_select_bitmask<M, T>(m: M, yes: T, no: T) -> T;
151+
152+
/// getelementptr (without inbounds)
153+
/// equivalent to wrapping_offset
154+
pub(crate) fn simd_arith_offset<T, U>(ptr: T, offset: U) -> T;
155+
156+
/// equivalent to `T as U` semantics, specifically for pointers
157+
pub(crate) fn simd_cast_ptr<T, U>(ptr: T) -> U;
158+
159+
/// expose a pointer as an address
160+
pub(crate) fn simd_expose_addr<T, U>(ptr: T) -> U;
161+
162+
/// convert an exposed address back to a pointer
163+
pub(crate) fn simd_from_exposed_addr<T, U>(addr: T) -> U;
154164
}

‎crates/core_simd/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
repr_simd,
88
simd_ffi,
99
staged_api,
10-
stdsimd
10+
stdsimd,
11+
strict_provenance,
12+
ptr_metadata
1113
)]
1214
#![cfg_attr(feature = "generic_const_exprs", feature(generic_const_exprs))]
1315
#![cfg_attr(feature = "generic_const_exprs", allow(incomplete_features))]

‎crates/core_simd/src/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub(crate) mod intrinsics;
77
mod to_bytes;
88

99
mod alias;
10+
mod cast;
1011
mod elements;
1112
mod eq;
1213
mod fmt;
@@ -24,6 +25,7 @@ pub mod simd {
2425
pub(crate) use crate::core_simd::intrinsics;
2526

2627
pub use crate::core_simd::alias::*;
28+
pub use crate::core_simd::cast::*;
2729
pub use crate::core_simd::elements::*;
2830
pub use crate::core_simd::eq::*;
2931
pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount};

‎crates/core_simd/src/ord.rs

+101-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount};
1+
use crate::simd::{
2+
intrinsics, LaneCount, Mask, Simd, SimdConstPtr, SimdMutPtr, SimdPartialEq, SupportedLaneCount,
3+
};
24

35
/// Parallel `PartialOrd`.
46
pub trait SimdPartialOrd: SimdPartialEq {
@@ -211,3 +213,101 @@ macro_rules! impl_mask {
211213
}
212214

213215
impl_mask! { i8, i16, i32, i64, isize }
216+
217+
impl<T, const LANES: usize> SimdPartialOrd for Simd<*const T, LANES>
218+
where
219+
LaneCount<LANES>: SupportedLaneCount,
220+
{
221+
#[inline]
222+
fn simd_lt(self, other: Self) -> Self::Mask {
223+
self.addr().simd_lt(other.addr())
224+
}
225+
226+
#[inline]
227+
fn simd_le(self, other: Self) -> Self::Mask {
228+
self.addr().simd_le(other.addr())
229+
}
230+
231+
#[inline]
232+
fn simd_gt(self, other: Self) -> Self::Mask {
233+
self.addr().simd_gt(other.addr())
234+
}
235+
236+
#[inline]
237+
fn simd_ge(self, other: Self) -> Self::Mask {
238+
self.addr().simd_ge(other.addr())
239+
}
240+
}
241+
242+
impl<T, const LANES: usize> SimdOrd for Simd<*const T, LANES>
243+
where
244+
LaneCount<LANES>: SupportedLaneCount,
245+
{
246+
#[inline]
247+
fn simd_max(self, other: Self) -> Self {
248+
self.simd_lt(other).select(other, self)
249+
}
250+
251+
#[inline]
252+
fn simd_min(self, other: Self) -> Self {
253+
self.simd_gt(other).select(other, self)
254+
}
255+
256+
#[inline]
257+
fn simd_clamp(self, min: Self, max: Self) -> Self {
258+
assert!(
259+
min.simd_le(max).all(),
260+
"each lane in `min` must be less than or equal to the corresponding lane in `max`",
261+
);
262+
self.simd_max(min).simd_min(max)
263+
}
264+
}
265+
266+
impl<T, const LANES: usize> SimdPartialOrd for Simd<*mut T, LANES>
267+
where
268+
LaneCount<LANES>: SupportedLaneCount,
269+
{
270+
#[inline]
271+
fn simd_lt(self, other: Self) -> Self::Mask {
272+
self.addr().simd_lt(other.addr())
273+
}
274+
275+
#[inline]
276+
fn simd_le(self, other: Self) -> Self::Mask {
277+
self.addr().simd_le(other.addr())
278+
}
279+
280+
#[inline]
281+
fn simd_gt(self, other: Self) -> Self::Mask {
282+
self.addr().simd_gt(other.addr())
283+
}
284+
285+
#[inline]
286+
fn simd_ge(self, other: Self) -> Self::Mask {
287+
self.addr().simd_ge(other.addr())
288+
}
289+
}
290+
291+
impl<T, const LANES: usize> SimdOrd for Simd<*mut T, LANES>
292+
where
293+
LaneCount<LANES>: SupportedLaneCount,
294+
{
295+
#[inline]
296+
fn simd_max(self, other: Self) -> Self {
297+
self.simd_lt(other).select(other, self)
298+
}
299+
300+
#[inline]
301+
fn simd_min(self, other: Self) -> Self {
302+
self.simd_gt(other).select(other, self)
303+
}
304+
305+
#[inline]
306+
fn simd_clamp(self, min: Self, max: Self) -> Self {
307+
assert!(
308+
min.simd_le(max).all(),
309+
"each lane in `min` must be less than or equal to the corresponding lane in `max`",
310+
);
311+
self.simd_max(min).simd_min(max)
312+
}
313+
}

‎crates/core_simd/src/vector.rs

+48-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
// Vectors of pointers are not for public use at the current time.
2-
pub(crate) mod ptr;
3-
41
use crate::simd::{
5-
intrinsics, LaneCount, Mask, MaskElement, SimdPartialOrd, SupportedLaneCount, Swizzle,
2+
intrinsics, LaneCount, Mask, MaskElement, SimdCast, SimdCastPtr, SimdConstPtr, SimdMutPtr,
3+
SimdPartialOrd, SupportedLaneCount, Swizzle,
64
};
75

86
/// A SIMD vector of `LANES` elements of type `T`. `Simd<T, N>` has the same shape as [`[T; N]`](array), but operates like `T`.
@@ -211,11 +209,26 @@ where
211209
#[must_use]
212210
#[inline]
213211
#[cfg(not(bootstrap))]
214-
pub fn cast<U: SimdElement>(self) -> Simd<U, LANES> {
215-
// Safety: The input argument is a vector of a valid SIMD element type.
212+
pub fn cast<U: SimdCast>(self) -> Simd<U, LANES>
213+
where
214+
T: SimdCast,
215+
{
216+
// Safety: supported types are guaranteed by SimdCast
216217
unsafe { intrinsics::simd_as(self) }
217218
}
218219

220+
/// Lanewise casts pointers to another pointer type.
221+
#[must_use]
222+
#[inline]
223+
pub fn cast_ptr<U>(self) -> Simd<U, LANES>
224+
where
225+
T: SimdCastPtr<U>,
226+
U: SimdElement,
227+
{
228+
// Safety: supported types are guaranteed by SimdCastPtr
229+
unsafe { intrinsics::simd_cast_ptr(self) }
230+
}
231+
219232
/// Rounds toward zero and converts to the same-width integer type, assuming that
220233
/// the value is finite and fits in that type.
221234
///
@@ -234,11 +247,10 @@ where
234247
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
235248
pub unsafe fn to_int_unchecked<I>(self) -> Simd<I, LANES>
236249
where
237-
T: core::convert::FloatToInt<I>,
238-
I: SimdElement,
250+
T: core::convert::FloatToInt<I> + SimdCast,
251+
I: SimdCast,
239252
{
240-
// Safety: `self` is a vector, and `FloatToInt` ensures the type can be casted to
241-
// an integer.
253+
// Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants
242254
unsafe { intrinsics::simd_cast(self) }
243255
}
244256

@@ -349,7 +361,7 @@ where
349361
idxs: Simd<usize, LANES>,
350362
or: Self,
351363
) -> Self {
352-
let base_ptr = crate::simd::ptr::SimdConstPtr::splat(slice.as_ptr());
364+
let base_ptr = Simd::<*const T, LANES>::splat(slice.as_ptr());
353365
// Ferris forgive me, I have done pointer arithmetic here.
354366
let ptrs = base_ptr.wrapping_add(idxs);
355367
// Safety: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
@@ -457,7 +469,7 @@ where
457469
// 3. &mut [T] which will become our base ptr.
458470
unsafe {
459471
// Now Entering ☢️ *mut T Zone
460-
let base_ptr = crate::simd::ptr::SimdMutPtr::splat(slice.as_mut_ptr());
472+
let base_ptr = Simd::<*mut T, LANES>::splat(slice.as_mut_ptr());
461473
// Ferris forgive me, I have done pointer arithmetic here.
462474
let ptrs = base_ptr.wrapping_add(idxs);
463475
// The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
@@ -739,3 +751,27 @@ impl Sealed for f64 {}
739751
unsafe impl SimdElement for f64 {
740752
type Mask = i64;
741753
}
754+
755+
impl<T> Sealed for *const T {}
756+
757+
// Safety: (thin) const pointers are valid SIMD element types, and are supported by this API
758+
//
759+
// Fat pointers may be supported in the future.
760+
unsafe impl<T> SimdElement for *const T
761+
where
762+
T: core::ptr::Pointee<Metadata = ()>,
763+
{
764+
type Mask = isize;
765+
}
766+
767+
impl<T> Sealed for *mut T {}
768+
769+
// Safety: (thin) mut pointers are valid SIMD element types, and are supported by this API
770+
//
771+
// Fat pointers may be supported in the future.
772+
unsafe impl<T> SimdElement for *mut T
773+
where
774+
T: core::ptr::Pointee<Metadata = ()>,
775+
{
776+
type Mask = isize;
777+
}

‎crates/core_simd/src/vector/ptr.rs

-51
This file was deleted.

‎crates/core_simd/tests/pointers.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#![feature(portable_simd, strict_provenance)]
2+
3+
use core_simd::{Simd, SimdConstPtr, SimdMutPtr};
4+
5+
macro_rules! common_tests {
6+
{ $constness:ident } => {
7+
test_helpers::test_lanes! {
8+
fn is_null<const LANES: usize>() {
9+
test_helpers::test_unary_mask_elementwise(
10+
&Simd::<*$constness u32, LANES>::is_null,
11+
&<*$constness u32>::is_null,
12+
&|_| true,
13+
);
14+
}
15+
16+
fn addr<const LANES: usize>() {
17+
test_helpers::test_unary_elementwise(
18+
&Simd::<*$constness u32, LANES>::addr,
19+
&<*$constness u32>::addr,
20+
&|_| true,
21+
);
22+
}
23+
24+
fn wrapping_offset<const LANES: usize>() {
25+
test_helpers::test_binary_elementwise(
26+
&Simd::<*$constness u32, LANES>::wrapping_offset,
27+
&<*$constness u32>::wrapping_offset,
28+
&|_, _| true,
29+
);
30+
}
31+
32+
fn wrapping_add<const LANES: usize>() {
33+
test_helpers::test_binary_elementwise(
34+
&Simd::<*$constness u32, LANES>::wrapping_add,
35+
&<*$constness u32>::wrapping_add,
36+
&|_, _| true,
37+
);
38+
}
39+
40+
fn wrapping_sub<const LANES: usize>() {
41+
test_helpers::test_binary_elementwise(
42+
&Simd::<*$constness u32, LANES>::wrapping_sub,
43+
&<*$constness u32>::wrapping_sub,
44+
&|_, _| true,
45+
);
46+
}
47+
}
48+
}
49+
}
50+
51+
mod const_ptr {
52+
use super::*;
53+
common_tests! { const }
54+
}
55+
56+
mod mut_ptr {
57+
use super::*;
58+
common_tests! { mut }
59+
}

‎crates/test_helpers/src/biteq.rs

+20
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ macro_rules! impl_float_biteq {
5555

5656
impl_float_biteq! { f32, f64 }
5757

58+
impl<T> BitEq for *const T {
59+
fn biteq(&self, other: &Self) -> bool {
60+
self == other
61+
}
62+
63+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
64+
write!(f, "{:?}", self)
65+
}
66+
}
67+
68+
impl<T> BitEq for *mut T {
69+
fn biteq(&self, other: &Self) -> bool {
70+
self == other
71+
}
72+
73+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
74+
write!(f, "{:?}", self)
75+
}
76+
}
77+
5878
impl<T: BitEq, const N: usize> BitEq for [T; N] {
5979
fn biteq(&self, other: &Self) -> bool {
6080
self.iter()

‎crates/test_helpers/src/lib.rs

+43-20
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,28 @@ impl_num! { usize }
3838
impl_num! { f32 }
3939
impl_num! { f64 }
4040

41+
impl<T> DefaultStrategy for *const T {
42+
type Strategy = proptest::strategy::Map<proptest::num::isize::Any, fn(isize) -> *const T>;
43+
fn default_strategy() -> Self::Strategy {
44+
fn map<T>(x: isize) -> *const T {
45+
x as _
46+
}
47+
use proptest::strategy::Strategy;
48+
proptest::num::isize::ANY.prop_map(map)
49+
}
50+
}
51+
52+
impl<T> DefaultStrategy for *mut T {
53+
type Strategy = proptest::strategy::Map<proptest::num::isize::Any, fn(isize) -> *mut T>;
54+
fn default_strategy() -> Self::Strategy {
55+
fn map<T>(x: isize) -> *mut T {
56+
x as _
57+
}
58+
use proptest::strategy::Strategy;
59+
proptest::num::isize::ANY.prop_map(map)
60+
}
61+
}
62+
4163
#[cfg(not(target_arch = "wasm32"))]
4264
impl DefaultStrategy for u128 {
4365
type Strategy = proptest::num::u128::Any;
@@ -135,21 +157,21 @@ pub fn test_unary_elementwise<Scalar, ScalarResult, Vector, VectorResult, const
135157
fs: &dyn Fn(Scalar) -> ScalarResult,
136158
check: &dyn Fn([Scalar; LANES]) -> bool,
137159
) where
138-
Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy,
139-
ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
160+
Scalar: Copy + core::fmt::Debug + DefaultStrategy,
161+
ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
140162
Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy,
141163
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
142164
{
143165
test_1(&|x: [Scalar; LANES]| {
144166
proptest::prop_assume!(check(x));
145167
let result_1: [ScalarResult; LANES] = fv(x.into()).into();
146-
let result_2: [ScalarResult; LANES] = {
147-
let mut result = [ScalarResult::default(); LANES];
148-
for (i, o) in x.iter().zip(result.iter_mut()) {
149-
*o = fs(*i);
150-
}
151-
result
152-
};
168+
let result_2: [ScalarResult; LANES] = x
169+
.iter()
170+
.copied()
171+
.map(fs)
172+
.collect::<Vec<_>>()
173+
.try_into()
174+
.unwrap();
153175
crate::prop_assert_biteq!(result_1, result_2);
154176
Ok(())
155177
});
@@ -162,7 +184,7 @@ pub fn test_unary_mask_elementwise<Scalar, Vector, Mask, const LANES: usize>(
162184
fs: &dyn Fn(Scalar) -> bool,
163185
check: &dyn Fn([Scalar; LANES]) -> bool,
164186
) where
165-
Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy,
187+
Scalar: Copy + core::fmt::Debug + DefaultStrategy,
166188
Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy,
167189
Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy,
168190
{
@@ -196,23 +218,24 @@ pub fn test_binary_elementwise<
196218
fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult,
197219
check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool,
198220
) where
199-
Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
200-
Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
201-
ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
221+
Scalar1: Copy + core::fmt::Debug + DefaultStrategy,
222+
Scalar2: Copy + core::fmt::Debug + DefaultStrategy,
223+
ScalarResult: Copy + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
202224
Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
203225
Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
204226
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
205227
{
206228
test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| {
207229
proptest::prop_assume!(check(x, y));
208230
let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into();
209-
let result_2: [ScalarResult; LANES] = {
210-
let mut result = [ScalarResult::default(); LANES];
211-
for ((i1, i2), o) in x.iter().zip(y.iter()).zip(result.iter_mut()) {
212-
*o = fs(*i1, *i2);
213-
}
214-
result
215-
};
231+
let result_2: [ScalarResult; LANES] = x
232+
.iter()
233+
.copied()
234+
.zip(y.iter().copied())
235+
.map(|(x, y)| fs(x, y))
236+
.collect::<Vec<_>>()
237+
.try_into()
238+
.unwrap();
216239
crate::prop_assert_biteq!(result_1, result_2);
217240
Ok(())
218241
});

0 commit comments

Comments
 (0)
Please sign in to comment.