]> git.lizzy.rs Git - rust.git/commitdiff
implement simd_cast, simd_as
authorRalf Jung <post@ralfj.de>
Sun, 6 Mar 2022 18:58:41 +0000 (13:58 -0500)
committerRalf Jung <post@ralfj.de>
Mon, 7 Mar 2022 14:40:23 +0000 (09:40 -0500)
src/shims/intrinsics.rs
tests/compile-fail/intrinsics/simd-float-to-int.rs [new file with mode: 0644]
tests/run-pass/portable-simd.rs

index 670e3cb1b8acb7ab616801148570ca3009bd1f0b..2f29ec4553d48cdf8de1b01ecaa0ffadf440707e 100644 (file)
@@ -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 (file)
index 0000000..88d5a7a
--- /dev/null
@@ -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();
+} }
index 5d7420604bb7aa01817c1af5e0e2dcfc7d7354da..022e8c91f970dee08f2d824cdbbce49b991eb6a4 100644 (file)
@@ -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<T, U>(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();
 }