From db06d4998fa8e64c290a7ae439fb2f8aefb2223e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 6 Mar 2022 13:58:41 -0500 Subject: [PATCH] implement simd_cast, simd_as --- src/shims/intrinsics.rs | 39 +++++ .../intrinsics/simd-float-to-int.rs | 7 + tests/run-pass/portable-simd.rs | 141 ++++++++++++++---- 3 files changed, 161 insertions(+), 26 deletions(-) create mode 100644 tests/compile-fail/intrinsics/simd-float-to-int.rs diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index 670e3cb1b8a..2f29ec4553d 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -515,6 +515,45 @@ enum Op { this.write_immediate(*val, &dest.into())?; } } + #[rustfmt::skip] + "simd_cast" | "simd_as" => { + let &[ref op] = check_arg_count(args)?; + let (op, op_len) = this.operand_to_simd(op)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, op_len); + + let safe_cast = intrinsic_name == "simd_as"; + + for i in 0..dest_len { + let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + + let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { + // Int-to-(int|float): always safe + (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) => + this.misc_cast(&op, dest.layout.ty)?, + // Float-to-float: always safe + (ty::Float(_), ty::Float(_)) => + this.misc_cast(&op, dest.layout.ty)?, + // Float-to-int in safe mode + (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => + this.misc_cast(&op, dest.layout.ty)?, + // Float-to-int in unchecked mode + (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if !safe_cast => + this.float_to_int_unchecked(op.to_scalar()?.to_f32()?, dest.layout.ty)?.into(), + (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if !safe_cast => + this.float_to_int_unchecked(op.to_scalar()?.to_f64()?, dest.layout.ty)?.into(), + _ => + throw_unsup_format!( + "Unsupported SIMD cast from element type {} to {}", + op.layout.ty, + dest.layout.ty + ), + }; + this.write_immediate(val, &dest.into())?; + } + } // Atomic operations "atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?, diff --git a/tests/compile-fail/intrinsics/simd-float-to-int.rs b/tests/compile-fail/intrinsics/simd-float-to-int.rs new file mode 100644 index 00000000000..88d5a7a466f --- /dev/null +++ b/tests/compile-fail/intrinsics/simd-float-to-int.rs @@ -0,0 +1,7 @@ +// error-pattern: cannot be represented in target type `i32` +#![feature(portable_simd)] +use std::simd::*; + +fn main() { unsafe { + let _x : i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); +} } diff --git a/tests/run-pass/portable-simd.rs b/tests/run-pass/portable-simd.rs index 5d7420604bb..022e8c91f97 100644 --- a/tests/run-pass/portable-simd.rs +++ b/tests/run-pass/portable-simd.rs @@ -13,17 +13,16 @@ fn simd_ops_f32() { assert_eq!(a % b, f32x4::from_array([0.0, 0.0, 1.0, 2.0])); assert_eq!(b.abs(), f32x4::from_array([1.0, 2.0, 3.0, 4.0])); - // FIXME use Mask::from_array once simd_cast is implemented. - assert_eq!(a.lanes_eq(f32x4::splat(5.0)*b), Mask::from_int(i32x4::from_array([0, -1, 0, 0]))); - assert_eq!(a.lanes_ne(f32x4::splat(5.0)*b), Mask::from_int(i32x4::from_array([-1, 0, -1, -1]))); - assert_eq!(a.lanes_le(f32x4::splat(5.0)*b), Mask::from_int(i32x4::from_array([0, -1, -1, 0]))); - assert_eq!(a.lanes_lt(f32x4::splat(5.0)*b), Mask::from_int(i32x4::from_array([0, 0, -1, 0]))); - assert_eq!(a.lanes_ge(f32x4::splat(5.0)*b), Mask::from_int(i32x4::from_array([-1, -1, 0, -1]))); - assert_eq!(a.lanes_gt(f32x4::splat(5.0)*b), Mask::from_int(i32x4::from_array([-1, 0, 0, -1]))); + assert_eq!(a.lanes_eq(f32x4::splat(5.0) * b), Mask::from_array([false, true, false, false])); + assert_eq!(a.lanes_ne(f32x4::splat(5.0) * b), Mask::from_array([true, false, true, true])); + assert_eq!(a.lanes_le(f32x4::splat(5.0) * b), Mask::from_array([false, true, true, false])); + assert_eq!(a.lanes_lt(f32x4::splat(5.0) * b), Mask::from_array([false, false, true, false])); + assert_eq!(a.lanes_ge(f32x4::splat(5.0) * b), Mask::from_array([true, true, false, true])); + assert_eq!(a.lanes_gt(f32x4::splat(5.0) * b), Mask::from_array([true, false, false, true])); assert_eq!(a.horizontal_sum(), 40.0); assert_eq!(b.horizontal_sum(), 2.0); - assert_eq!(a.horizontal_product(), 100.0*100.0); + assert_eq!(a.horizontal_product(), 100.0 * 100.0); assert_eq!(b.horizontal_product(), -24.0); } @@ -39,17 +38,16 @@ fn simd_ops_f64() { assert_eq!(a % b, f64x4::from_array([0.0, 0.0, 1.0, 2.0])); assert_eq!(b.abs(), f64x4::from_array([1.0, 2.0, 3.0, 4.0])); - // FIXME use Mask::from_array once simd_cast is implemented. - assert_eq!(a.lanes_eq(f64x4::splat(5.0)*b), Mask::from_int(i64x4::from_array([0, -1, 0, 0]))); - assert_eq!(a.lanes_ne(f64x4::splat(5.0)*b), Mask::from_int(i64x4::from_array([-1, 0, -1, -1]))); - assert_eq!(a.lanes_le(f64x4::splat(5.0)*b), Mask::from_int(i64x4::from_array([0, -1, -1, 0]))); - assert_eq!(a.lanes_lt(f64x4::splat(5.0)*b), Mask::from_int(i64x4::from_array([0, 0, -1, 0]))); - assert_eq!(a.lanes_ge(f64x4::splat(5.0)*b), Mask::from_int(i64x4::from_array([-1, -1, 0, -1]))); - assert_eq!(a.lanes_gt(f64x4::splat(5.0)*b), Mask::from_int(i64x4::from_array([-1, 0, 0, -1]))); + assert_eq!(a.lanes_eq(f64x4::splat(5.0) * b), Mask::from_array([false, true, false, false])); + assert_eq!(a.lanes_ne(f64x4::splat(5.0) * b), Mask::from_array([true, false, true, true])); + assert_eq!(a.lanes_le(f64x4::splat(5.0) * b), Mask::from_array([false, true, true, false])); + assert_eq!(a.lanes_lt(f64x4::splat(5.0) * b), Mask::from_array([false, false, true, false])); + assert_eq!(a.lanes_ge(f64x4::splat(5.0) * b), Mask::from_array([true, true, false, true])); + assert_eq!(a.lanes_gt(f64x4::splat(5.0) * b), Mask::from_array([true, false, false, true])); assert_eq!(a.horizontal_sum(), 40.0); assert_eq!(b.horizontal_sum(), 2.0); - assert_eq!(a.horizontal_product(), 100.0*100.0); + assert_eq!(a.horizontal_product(), 100.0 * 100.0); assert_eq!(b.horizontal_product(), -24.0); } @@ -71,13 +69,12 @@ fn simd_ops_i32() { assert_eq!(b | i32x4::splat(2), i32x4::from_array([3, 2, 3, -2])); assert_eq!(b ^ i32x4::splat(2), i32x4::from_array([3, 0, 1, -2])); - // FIXME use Mask::from_array once simd_cast is implemented. - assert_eq!(a.lanes_eq(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([0, -1, 0, 0]))); - assert_eq!(a.lanes_ne(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([-1, 0, -1, -1]))); - assert_eq!(a.lanes_le(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([0, -1, -1, 0]))); - assert_eq!(a.lanes_lt(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([0, 0, -1, 0]))); - assert_eq!(a.lanes_ge(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([-1, -1, 0, -1]))); - assert_eq!(a.lanes_gt(i32x4::splat(5)*b), Mask::from_int(i32x4::from_array([-1, 0, 0, -1]))); + assert_eq!(a.lanes_eq(i32x4::splat(5) * b), Mask::from_array([false, true, false, false])); + assert_eq!(a.lanes_ne(i32x4::splat(5) * b), Mask::from_array([true, false, true, true])); + assert_eq!(a.lanes_le(i32x4::splat(5) * b), Mask::from_array([false, true, true, false])); + assert_eq!(a.lanes_lt(i32x4::splat(5) * b), Mask::from_array([false, false, true, false])); + assert_eq!(a.lanes_ge(i32x4::splat(5) * b), Mask::from_array([true, true, false, true])); + assert_eq!(a.lanes_gt(i32x4::splat(5) * b), Mask::from_array([true, false, false, true])); assert_eq!(a.horizontal_and(), 10); assert_eq!(b.horizontal_and(), 0); @@ -87,10 +84,94 @@ fn simd_ops_i32() { assert_eq!(b.horizontal_xor(), -4); assert_eq!(a.horizontal_sum(), 40); assert_eq!(b.horizontal_sum(), 2); - assert_eq!(a.horizontal_product(), 100*100); + assert_eq!(a.horizontal_product(), 100 * 100); assert_eq!(b.horizontal_product(), -24); } +fn simd_mask() { + let intmask = Mask::from_int(i32x4::from_array([0, -1, 0, 0])); + assert_eq!(intmask, Mask::from_array([false, true, false, false])); + assert_eq!(intmask.to_array(), [false, true, false, false]); +} + +fn simd_cast() { + // between integer types + assert_eq!(i32x4::from_array([1, 2, 3, -4]), i16x4::from_array([1, 2, 3, -4]).cast()); + assert_eq!(i16x4::from_array([1, 2, 3, -4]), i32x4::from_array([1, 2, 3, -4]).cast()); + assert_eq!(i32x4::from_array([1, -1, 3, 4]), u64x4::from_array([1, u64::MAX, 3, 4]).cast()); + + // float -> int + assert_eq!( + i8x4::from_array([127, -128, 127, -128]), + f32x4::from_array([127.99, -128.99, 999.0, -999.0]).cast() + ); + assert_eq!( + i32x4::from_array([0, 1, -1, 2147483520]), + f32x4::from_array([ + -0.0, + /*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), + /*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), + 2147483520.0 + ]) + .cast() + ); + assert_eq!( + i32x8::from_array([i32::MAX, i32::MIN, i32::MAX, i32::MIN, i32::MAX, i32::MIN, 0, 0]), + f32x8::from_array([ + 2147483648.0f32, + -2147483904.0f32, + f32::MAX, + f32::MIN, + f32::INFINITY, + f32::NEG_INFINITY, + f32::NAN, + -f32::NAN, + ]) + .cast() + ); + + // int -> float + assert_eq!( + f32x4::from_array([ + -2147483648.0, + /*0x1.26580cp+30*/ f32::from_bits(0x4e932c06), + 16777220.0, + -16777220.0, + ]), + i32x4::from_array([-2147483647i32, 1234567890i32, 16777219i32, -16777219i32]).cast() + ); + + // float -> float + assert_eq!( + f32x4::from_array([f32::INFINITY, f32::INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY]), + f64x4::from_array([f64::MAX, f64::INFINITY, f64::MIN, f64::NEG_INFINITY]).cast() + ); + + // unchecked casts + unsafe { + assert_eq!( + i32x4::from_array([0, 1, -1, 2147483520]), + f32x4::from_array([ + -0.0, + /*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), + /*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), + 2147483520.0 + ]) + .to_int_unchecked() + ); + assert_eq!( + u64x4::from_array([0, 10000000000000000, u64::MAX - 2047, 9223372036854775808]), + f64x4::from_array([ + -0.99999999999, + 1e16, + (u64::MAX - 1024) as f64, + 9223372036854775808.0 + ]) + .to_int_unchecked() + ); + } +} + fn simd_intrinsics() { extern "platform-intrinsic" { fn simd_eq(x: T, y: T) -> U; @@ -112,14 +193,22 @@ fn simd_intrinsics() { assert!(simd_reduce_all(i32x4::splat(-1))); assert!(!simd_reduce_all(i32x2::from_array([0, -1]))); - assert_eq!(simd_select(i8x4::from_array([0, -1, -1, 0]), a, b), i32x4::from_array([1, 10, 10, 4])); - assert_eq!(simd_select(i8x4::from_array([0, -1, -1, 0]), b, a), i32x4::from_array([10, 2, 10, 10])); + assert_eq!( + simd_select(i8x4::from_array([0, -1, -1, 0]), a, b), + i32x4::from_array([1, 10, 10, 4]) + ); + assert_eq!( + simd_select(i8x4::from_array([0, -1, -1, 0]), b, a), + i32x4::from_array([10, 2, 10, 10]) + ); } } fn main() { + simd_mask(); simd_ops_f32(); simd_ops_f64(); simd_ops_i32(); + simd_cast(); simd_intrinsics(); }