|
1 |
| -use crate::simd::intrinsics; |
2 |
| -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; |
| 1 | +use crate::simd::{LaneCount, Mask, Simd, SimdElement, SupportedLaneCount}; |
3 | 2 | use core::ops::{Add, Mul};
|
4 | 3 | use core::ops::{BitAnd, BitOr, BitXor};
|
5 | 4 | use core::ops::{Div, Rem, Sub};
|
@@ -284,145 +283,169 @@ float_arith! {
|
284 | 283 | }
|
285 | 284 | }
|
286 | 285 |
|
287 |
| -/// Automatically implements operators over references in addition to the provided operator. |
288 |
| -macro_rules! impl_ref_ops { |
289 |
| - // binary op |
290 |
| - { |
291 |
| - impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty |
292 |
| - where |
293 |
| - LaneCount<$lanes2:ident>: SupportedLaneCount, |
294 |
| - { |
295 |
| - type Output = $output:ty; |
296 |
| - |
297 |
| - $(#[$attrs:meta])* |
298 |
| - fn $fn:ident($self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) -> Self::Output $body:tt |
| 286 | +// Division by zero is poison, according to LLVM. |
| 287 | +// So is dividing the MIN value of a signed integer by -1, |
| 288 | +// since that would return MAX + 1. |
| 289 | +// FIXME: Rust allows <SInt>::MIN / -1, |
| 290 | +// so we should probably figure out how to make that safe. |
| 291 | +macro_rules! int_divrem_guard { |
| 292 | + ($(impl<const LANES: usize> $op:ident for Simd<$sint:ty, LANES> { |
| 293 | + const PANIC_ZERO: &'static str = $zero:literal; |
| 294 | + const PANIC_OVERFLOW: &'static str = $overflow:literal; |
| 295 | + fn $call:ident { |
| 296 | + unsafe { $simd_call:ident } |
299 | 297 | }
|
300 |
| - } => { |
301 |
| - impl<const $lanes: usize> core::ops::$trait<$rhs> for $type |
| 298 | + })*) => { |
| 299 | + $(impl<const LANES: usize> $op for Simd<$sint, LANES> |
302 | 300 | where
|
303 |
| - LaneCount<$lanes2>: SupportedLaneCount, |
| 301 | + $sint: SimdElement, |
| 302 | + LaneCount<LANES>: SupportedLaneCount, |
304 | 303 | {
|
305 |
| - type Output = $output; |
306 |
| - |
307 |
| - $(#[$attrs])* |
308 |
| - fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body |
309 |
| - } |
| 304 | + type Output = Self; |
| 305 | + #[inline] |
| 306 | + #[must_use = "operator returns a new vector without mutating the inputs"] |
| 307 | + fn $call(self, rhs: Self) -> Self::Output { |
| 308 | + if rhs.lanes_eq(Simd::splat(0)).any() { |
| 309 | + panic!("attempt to calculate the remainder with a divisor of zero"); |
| 310 | + } else if <$sint>::MIN != 0 && self.lanes_eq(Simd::splat(<$sint>::MIN)) & rhs.lanes_eq(Simd::splat(-1 as _)) |
| 311 | + != Mask::splat(false) |
| 312 | + { |
| 313 | + panic!("attempt to calculate the remainder with overflow"); |
| 314 | + } else { |
| 315 | + unsafe { $crate::intrinsics::$simd_call(self, rhs) } |
| 316 | + } |
| 317 | + } |
| 318 | + })* |
310 | 319 | };
|
311 | 320 | }
|
312 | 321 |
|
313 |
| -/// Automatically implements operators over vectors and scalars for a particular vector. |
314 |
| -macro_rules! impl_op { |
315 |
| - { impl Add for $scalar:ty } => { |
316 |
| - impl_op! { @binary $scalar, Add::add, simd_add } |
317 |
| - }; |
318 |
| - { impl Sub for $scalar:ty } => { |
319 |
| - impl_op! { @binary $scalar, Sub::sub, simd_sub } |
320 |
| - }; |
321 |
| - { impl Mul for $scalar:ty } => { |
322 |
| - impl_op! { @binary $scalar, Mul::mul, simd_mul } |
323 |
| - }; |
324 |
| - { impl Div for $scalar:ty } => { |
325 |
| - impl_op! { @binary $scalar, Div::div, simd_div } |
326 |
| - }; |
327 |
| - { impl Rem for $scalar:ty } => { |
328 |
| - impl_op! { @binary $scalar, Rem::rem, simd_rem } |
329 |
| - }; |
| 322 | +macro_rules! int_arith { |
| 323 | + ($(impl<const LANES: usize> IntArith for Simd<$sint:ty, LANES> { |
| 324 | + fn add(self, rhs: Self) -> Self::Output; |
| 325 | + fn mul(self, rhs: Self) -> Self::Output; |
| 326 | + fn sub(self, rhs: Self) -> Self::Output; |
| 327 | + fn div(self, rhs: Self) -> Self::Output; |
| 328 | + fn rem(self, rhs: Self) -> Self::Output; |
| 329 | + })*) => { |
| 330 | + $( |
| 331 | + unsafe_base_op!{ |
| 332 | + impl<const LANES: usize> Add for Simd<$sint, LANES> { |
| 333 | + fn add(self, rhs: Self) -> Self::Output { |
| 334 | + unsafe { simd_add } |
| 335 | + } |
| 336 | + } |
330 | 337 |
|
331 |
| - // generic binary op with assignment when output is `Self` |
332 |
| - { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $intrinsic:ident } => { |
333 |
| - impl_ref_ops! { |
334 |
| - impl<const LANES: usize> core::ops::$trait<Self> for Simd<$scalar, LANES> |
335 |
| - where |
336 |
| - LaneCount<LANES>: SupportedLaneCount, |
337 |
| - { |
338 |
| - type Output = Self; |
| 338 | + impl<const LANES: usize> Mul for Simd<$sint, LANES> { |
| 339 | + fn mul(self, rhs: Self) -> Self::Output { |
| 340 | + unsafe { simd_mul } |
| 341 | + } |
| 342 | + } |
339 | 343 |
|
340 |
| - #[inline] |
341 |
| - fn $trait_fn(self, rhs: Self) -> Self::Output { |
342 |
| - unsafe { |
343 |
| - intrinsics::$intrinsic(self, rhs) |
344 |
| - } |
| 344 | + impl<const LANES: usize> Sub for Simd<$sint, LANES> { |
| 345 | + fn sub(self, rhs: Self) -> Self::Output { |
| 346 | + unsafe { simd_sub } |
345 | 347 | }
|
346 | 348 | }
|
347 | 349 | }
|
348 |
| - }; |
349 |
| -} |
350 | 350 |
|
351 |
| -/// Implements unsigned integer operators for the provided types. |
352 |
| -macro_rules! impl_unsigned_int_ops { |
353 |
| - { $($scalar:ty),* } => { |
354 |
| - $( |
355 |
| - impl_op! { impl Add for $scalar } |
356 |
| - impl_op! { impl Sub for $scalar } |
357 |
| - impl_op! { impl Mul for $scalar } |
358 |
| - |
359 |
| - // Integers panic on divide by 0 |
360 |
| - impl_ref_ops! { |
361 |
| - impl<const LANES: usize> core::ops::Div<Self> for Simd<$scalar, LANES> |
362 |
| - where |
363 |
| - LaneCount<LANES>: SupportedLaneCount, |
364 |
| - { |
365 |
| - type Output = Self; |
366 |
| - |
367 |
| - #[inline] |
368 |
| - fn div(self, rhs: Self) -> Self::Output { |
369 |
| - if rhs.as_array() |
370 |
| - .iter() |
371 |
| - .any(|x| *x == 0) |
372 |
| - { |
373 |
| - panic!("attempt to divide by zero"); |
374 |
| - } |
375 |
| - |
376 |
| - // Guards for div(MIN, -1), |
377 |
| - // this check only applies to signed ints |
378 |
| - if <$scalar>::MIN != 0 && self.as_array().iter() |
379 |
| - .zip(rhs.as_array().iter()) |
380 |
| - .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) { |
381 |
| - panic!("attempt to divide with overflow"); |
382 |
| - } |
383 |
| - unsafe { intrinsics::simd_div(self, rhs) } |
384 |
| - } |
| 351 | + int_divrem_guard!{ |
| 352 | + impl<const LANES: usize> Div for Simd<$sint, LANES> { |
| 353 | + const PANIC_ZERO: &'static str = "attempt to divide by zero"; |
| 354 | + const PANIC_OVERFLOW: &'static str = "attempt to divide with overflow"; |
| 355 | + fn div { |
| 356 | + unsafe { simd_div } |
385 | 357 | }
|
386 | 358 | }
|
387 | 359 |
|
388 |
| - // remainder panics on zero divisor |
389 |
| - impl_ref_ops! { |
390 |
| - impl<const LANES: usize> core::ops::Rem<Self> for Simd<$scalar, LANES> |
391 |
| - where |
392 |
| - LaneCount<LANES>: SupportedLaneCount, |
393 |
| - { |
394 |
| - type Output = Self; |
395 |
| - |
396 |
| - #[inline] |
397 |
| - fn rem(self, rhs: Self) -> Self::Output { |
398 |
| - if rhs.as_array() |
399 |
| - .iter() |
400 |
| - .any(|x| *x == 0) |
401 |
| - { |
402 |
| - panic!("attempt to calculate the remainder with a divisor of zero"); |
403 |
| - } |
404 |
| - |
405 |
| - // Guards for rem(MIN, -1) |
406 |
| - // this branch applies the check only to signed ints |
407 |
| - if <$scalar>::MIN != 0 && self.as_array().iter() |
408 |
| - .zip(rhs.as_array().iter()) |
409 |
| - .any(|(x,y)| *x == <$scalar>::MIN && *y == -1 as _) { |
410 |
| - panic!("attempt to calculate the remainder with overflow"); |
411 |
| - } |
412 |
| - unsafe { intrinsics::simd_rem(self, rhs) } |
413 |
| - } |
| 360 | + impl<const LANES: usize> Rem for Simd<$sint, LANES> { |
| 361 | + const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; |
| 362 | + const PANIC_OVERFLOW: &'static str = "attempt to calculate the remainder with overflow"; |
| 363 | + fn rem { |
| 364 | + unsafe { simd_rem } |
414 | 365 | }
|
415 | 366 | }
|
416 |
| - )* |
417 |
| - }; |
| 367 | + })* |
| 368 | + } |
418 | 369 | }
|
419 | 370 |
|
420 |
| -/// Implements unsigned integer operators for the provided types. |
421 |
| -macro_rules! impl_signed_int_ops { |
422 |
| - { $($scalar:ty),* } => { |
423 |
| - impl_unsigned_int_ops! { $($scalar),* } |
424 |
| - }; |
425 |
| -} |
| 371 | +int_arith! { |
| 372 | + impl<const LANES: usize> IntArith for Simd<i8, LANES> { |
| 373 | + fn add(self, rhs: Self) -> Self::Output; |
| 374 | + fn mul(self, rhs: Self) -> Self::Output; |
| 375 | + fn sub(self, rhs: Self) -> Self::Output; |
| 376 | + fn div(self, rhs: Self) -> Self::Output; |
| 377 | + fn rem(self, rhs: Self) -> Self::Output; |
| 378 | + } |
426 | 379 |
|
427 |
| -impl_unsigned_int_ops! { u8, u16, u32, u64, usize } |
428 |
| -impl_signed_int_ops! { i8, i16, i32, i64, isize } |
| 380 | + impl<const LANES: usize> IntArith for Simd<i16, LANES> { |
| 381 | + fn add(self, rhs: Self) -> Self::Output; |
| 382 | + fn mul(self, rhs: Self) -> Self::Output; |
| 383 | + fn sub(self, rhs: Self) -> Self::Output; |
| 384 | + fn div(self, rhs: Self) -> Self::Output; |
| 385 | + fn rem(self, rhs: Self) -> Self::Output; |
| 386 | + } |
| 387 | + |
| 388 | + impl<const LANES: usize> IntArith for Simd<i32, LANES> { |
| 389 | + fn add(self, rhs: Self) -> Self::Output; |
| 390 | + fn mul(self, rhs: Self) -> Self::Output; |
| 391 | + fn sub(self, rhs: Self) -> Self::Output; |
| 392 | + fn div(self, rhs: Self) -> Self::Output; |
| 393 | + fn rem(self, rhs: Self) -> Self::Output; |
| 394 | + } |
| 395 | + |
| 396 | + impl<const LANES: usize> IntArith for Simd<i64, LANES> { |
| 397 | + fn add(self, rhs: Self) -> Self::Output; |
| 398 | + fn mul(self, rhs: Self) -> Self::Output; |
| 399 | + fn sub(self, rhs: Self) -> Self::Output; |
| 400 | + fn div(self, rhs: Self) -> Self::Output; |
| 401 | + fn rem(self, rhs: Self) -> Self::Output; |
| 402 | + } |
| 403 | + |
| 404 | + impl<const LANES: usize> IntArith for Simd<isize, LANES> { |
| 405 | + fn add(self, rhs: Self) -> Self::Output; |
| 406 | + fn mul(self, rhs: Self) -> Self::Output; |
| 407 | + fn sub(self, rhs: Self) -> Self::Output; |
| 408 | + fn div(self, rhs: Self) -> Self::Output; |
| 409 | + fn rem(self, rhs: Self) -> Self::Output; |
| 410 | + } |
| 411 | + |
| 412 | + impl<const LANES: usize> IntArith for Simd<u8, LANES> { |
| 413 | + fn add(self, rhs: Self) -> Self::Output; |
| 414 | + fn mul(self, rhs: Self) -> Self::Output; |
| 415 | + fn sub(self, rhs: Self) -> Self::Output; |
| 416 | + fn div(self, rhs: Self) -> Self::Output; |
| 417 | + fn rem(self, rhs: Self) -> Self::Output; |
| 418 | + } |
| 419 | + |
| 420 | + impl<const LANES: usize> IntArith for Simd<u16, LANES> { |
| 421 | + fn add(self, rhs: Self) -> Self::Output; |
| 422 | + fn mul(self, rhs: Self) -> Self::Output; |
| 423 | + fn sub(self, rhs: Self) -> Self::Output; |
| 424 | + fn div(self, rhs: Self) -> Self::Output; |
| 425 | + fn rem(self, rhs: Self) -> Self::Output; |
| 426 | + } |
| 427 | + |
| 428 | + impl<const LANES: usize> IntArith for Simd<u32, LANES> { |
| 429 | + fn add(self, rhs: Self) -> Self::Output; |
| 430 | + fn mul(self, rhs: Self) -> Self::Output; |
| 431 | + fn sub(self, rhs: Self) -> Self::Output; |
| 432 | + fn div(self, rhs: Self) -> Self::Output; |
| 433 | + fn rem(self, rhs: Self) -> Self::Output; |
| 434 | + } |
| 435 | + |
| 436 | + impl<const LANES: usize> IntArith for Simd<u64, LANES> { |
| 437 | + fn add(self, rhs: Self) -> Self::Output; |
| 438 | + fn mul(self, rhs: Self) -> Self::Output; |
| 439 | + fn sub(self, rhs: Self) -> Self::Output; |
| 440 | + fn div(self, rhs: Self) -> Self::Output; |
| 441 | + fn rem(self, rhs: Self) -> Self::Output; |
| 442 | + } |
| 443 | + |
| 444 | + impl<const LANES: usize> IntArith for Simd<usize, LANES> { |
| 445 | + fn add(self, rhs: Self) -> Self::Output; |
| 446 | + fn mul(self, rhs: Self) -> Self::Output; |
| 447 | + fn sub(self, rhs: Self) -> Self::Output; |
| 448 | + fn div(self, rhs: Self) -> Self::Output; |
| 449 | + fn rem(self, rhs: Self) -> Self::Output; |
| 450 | + } |
| 451 | +} |
0 commit comments