@@ -176,34 +176,62 @@ where
176
176
unsafe { & mut * ( self as * mut Self as * mut [ T ; N ] ) }
177
177
}
178
178
179
+ /// Load a vector from an array of `T`.
180
+ ///
181
+ /// This function is necessary since `repr(simd)` has padding for non-power-of-2 vectors (at the time of writing).
182
+ /// With padding, `read_unaligned` will read past the end of an array of N elements.
183
+ ///
184
+ /// # Safety
185
+ /// Reading `ptr` must be safe, as if by `<*const [T; N]>::read_unaligned`.
186
+ const unsafe fn load ( ptr : * const [ T ; N ] ) -> Self {
187
+ let mut tmp = core:: mem:: MaybeUninit :: uninit ( ) ;
188
+ // SAFETY: `Simd<T, N>` always contains `N` elements of type `T`. It may have padding
189
+ // which does not need to be initialized. The safety of reading `ptr` is ensured by the
190
+ // caller.
191
+ unsafe {
192
+ core:: ptr:: copy_nonoverlapping ( ptr, tmp. as_mut_ptr ( ) as * mut _ , 1 ) ;
193
+ tmp. assume_init ( )
194
+ }
195
+ }
196
+
197
+ /// Store a vector to an array of `T`.
198
+ ///
199
+ /// See `load` as to why this function is necessary.
200
+ ///
201
+ /// # Safety
202
+ /// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write_unaligned`.
203
+ const unsafe fn store ( self , ptr : * mut [ T ; N ] ) {
204
+ // SAFETY: `Simd<T, N>` always contains `N` elements of type `T`. The safety of writing
205
+ // `ptr` is ensured by the caller.
206
+ unsafe { core:: ptr:: copy_nonoverlapping ( self . as_array ( ) , ptr, 1 ) }
207
+ }
208
+
179
209
/// Converts an array to a SIMD vector.
180
210
pub const fn from_array ( array : [ T ; N ] ) -> Self {
181
- // SAFETY: Transmuting between `Simd<T, N>` and `[T; N]`
182
- // is always valid. We need to use `read_unaligned` here, since
183
- // the array may have a lower alignment than the vector.
211
+ // SAFETY: `&array` is safe to read.
184
212
//
185
- // FIXME: We currently use a pointer read instead of `transmute_copy` because
186
- // it results in better codegen with optimizations disabled, but we should
187
- // probably just use `transmute` once that works on const generic types.
213
+ // FIXME: We currently use a pointer load instead of `transmute_copy` because `repr(simd)`
214
+ // results in padding for non-power-of-2 vectors (so vectors are larger than arrays).
188
215
//
189
216
// NOTE: This deliberately doesn't just use `Self(array)`, see the comment
190
217
// on the struct definition for details.
191
- unsafe { ( & array as * const [ T ; N ] as * const Self ) . read_unaligned ( ) }
218
+ unsafe { Self :: load ( & array) }
192
219
}
193
220
194
221
/// Converts a SIMD vector to an array.
195
222
pub const fn to_array ( self ) -> [ T ; N ] {
196
- // SAFETY: Transmuting between `Simd<T, N>` and `[T; N]`
197
- // is always valid. No need to use `read_unaligned` here, since
198
- // the vector never has a lower alignment than the array.
223
+ let mut tmp = core:: mem:: MaybeUninit :: uninit ( ) ;
224
+ // SAFETY: writing to `tmp` is safe and initializes it.
199
225
//
200
- // FIXME: We currently use a pointer read instead of `transmute_copy` because
201
- // it results in better codegen with optimizations disabled, but we should
202
- // probably just use `transmute` once that works on const generic types.
226
+ // FIXME: We currently use a pointer store instead of `transmute_copy` because `repr(simd)`
227
+ // results in padding for non-power-of-2 vectors (so vectors are larger than arrays).
203
228
//
204
229
// NOTE: This deliberately doesn't just use `self.0`, see the comment
205
230
// on the struct definition for details.
206
- unsafe { ( & self as * const Self as * const [ T ; N ] ) . read ( ) }
231
+ unsafe {
232
+ self . store ( tmp. as_mut_ptr ( ) ) ;
233
+ tmp. assume_init ( )
234
+ }
207
235
}
208
236
209
237
/// Converts a slice to a SIMD vector containing `slice[..N]`.
0 commit comments