diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index f713721f584..5bebc52b782 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -356,7 +356,15 @@ fn call_intrinsic( | "simd_shr" | "simd_and" | "simd_or" - | "simd_eq" => { + | "simd_xor" + | "simd_eq" + | "simd_ne" + | "simd_lt" + | "simd_le" + | "simd_gt" + | "simd_ge" => { + use mir::BinOp; + let &[ref left, ref right] = check_arg_count(args)?; let (left, left_len) = this.operand_to_simd(left)?; let (right, right_len) = this.operand_to_simd(right)?; @@ -366,16 +374,22 @@ fn call_intrinsic( assert_eq!(dest_len, right_len); let op = match intrinsic_name { - "simd_add" => mir::BinOp::Add, - "simd_sub" => mir::BinOp::Sub, - "simd_mul" => mir::BinOp::Mul, - "simd_div" => mir::BinOp::Div, - "simd_rem" => mir::BinOp::Rem, - "simd_shl" => mir::BinOp::Shl, - "simd_shr" => mir::BinOp::Shr, - "simd_and" => mir::BinOp::BitAnd, - "simd_or" => mir::BinOp::BitOr, - "simd_eq" => mir::BinOp::Eq, + "simd_add" => BinOp::Add, + "simd_sub" => BinOp::Sub, + "simd_mul" => BinOp::Mul, + "simd_div" => BinOp::Div, + "simd_rem" => BinOp::Rem, + "simd_shl" => BinOp::Shl, + "simd_shr" => BinOp::Shr, + "simd_and" => BinOp::BitAnd, + "simd_or" => BinOp::BitOr, + "simd_xor" => BinOp::BitXor, + "simd_eq" => BinOp::Eq, + "simd_ne" => BinOp::Ne, + "simd_lt" => BinOp::Lt, + "simd_le" => BinOp::Le, + "simd_gt" => BinOp::Gt, + "simd_ge" => BinOp::Ge, _ => unreachable!(), }; @@ -384,7 +398,7 @@ fn call_intrinsic( let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?; let dest = this.mplace_index(&dest, i)?; let (val, overflowed, ty) = this.overflowing_binary_op(op, &left, &right)?; - if matches!(op, mir::BinOp::Shl | mir::BinOp::Shr) { + if matches!(op, BinOp::Shl | BinOp::Shr) { // Shifts have extra UB as SIMD operations that the MIR binop does not have. // See . if overflowed { @@ -392,27 +406,38 @@ fn call_intrinsic( throw_ub_format!("overflowing shift by {} in `{}` in SIMD lane {}", r_val, intrinsic_name, i); } } - if matches!(op, mir::BinOp::Eq) { + if matches!(op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) { // Special handling for boolean-returning operations assert_eq!(ty, this.tcx.types.bool); let val = val.to_bool().unwrap(); let val = bool_to_simd_element(val, dest.layout.size); this.write_scalar(val, &dest.into())?; } else { + assert_ne!(ty, this.tcx.types.bool); assert_eq!(ty, dest.layout.ty); this.write_scalar(val, &dest.into())?; } } } - "simd_reduce_any" => { + "simd_reduce_any" | "simd_reduce_all" => { let &[ref op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; - let mut res = false; // the neutral element + // the neutral element + let mut res = match intrinsic_name { + "simd_reduce_any" => false, + "simd_reduce_all" => true, + _ => bug!(), + }; + for i in 0..op_len { let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; let val = simd_element_to_bool(op)?; - res = res | val; + res = match intrinsic_name { + "simd_reduce_any" => res | val, + "simd_reduce_all" => res & val, + _ => bug!(), + }; } this.write_scalar(Scalar::from_bool(res), dest)?; diff --git a/tests/run-pass/portable-simd.rs b/tests/run-pass/portable-simd.rs index c0c1ecd0235..d43e7be8b19 100644 --- a/tests/run-pass/portable-simd.rs +++ b/tests/run-pass/portable-simd.rs @@ -12,6 +12,14 @@ fn simd_ops_f32() { assert_eq!(a / f32x4::splat(2.0), f32x4::splat(5.0)); 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]))); } fn simd_ops_f64() { @@ -25,30 +33,48 @@ fn simd_ops_f64() { assert_eq!(a / f64x4::splat(2.0), f64x4::splat(5.0)); 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]))); } fn simd_ops_i32() { let a = i32x4::splat(10); - let b = i32x4::from_array([1, 2, 3, 4]); - assert_eq!(-b, i32x4::from_array([-1, -2, -3, -4])); - assert_eq!(a + b, i32x4::from_array([11, 12, 13, 14])); - assert_eq!(a - b, i32x4::from_array([9, 8, 7, 6])); - assert_eq!(a * b, i32x4::from_array([10, 20, 30, 40])); - assert_eq!(a / b, i32x4::from_array([10, 5, 3, 2])); + let b = i32x4::from_array([1, 2, 3, -4]); + assert_eq!(-b, i32x4::from_array([-1, -2, -3, 4])); + assert_eq!(a + b, i32x4::from_array([11, 12, 13, 6])); + assert_eq!(a - b, i32x4::from_array([9, 8, 7, 14])); + assert_eq!(a * b, i32x4::from_array([10, 20, 30, -40])); + assert_eq!(a / b, i32x4::from_array([10, 5, 3, -2])); assert_eq!(a / i32x4::splat(2), i32x4::splat(5)); assert_eq!(i32x2::splat(i32::MIN) / i32x2::splat(-1), i32x2::splat(i32::MIN)); assert_eq!(a % b, i32x4::from_array([0, 0, 1, 2])); assert_eq!(i32x2::splat(i32::MIN) % i32x2::splat(-1), i32x2::splat(0)); - assert_eq!(b << i32x4::splat(2), i32x4::from_array([4, 8, 12, 16])); - assert_eq!(b >> i32x4::splat(1), i32x4::from_array([0, 1, 1, 2])); + assert_eq!(b << i32x4::splat(2), i32x4::from_array([4, 8, 12, -16])); + assert_eq!(b >> i32x4::splat(1), i32x4::from_array([0, 1, 1, -2])); assert_eq!(b & i32x4::splat(2), i32x4::from_array([0, 2, 2, 0])); - assert_eq!(b | i32x4::splat(2), i32x4::from_array([3, 2, 3, 6])); + 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]))); } fn simd_intrinsics() { extern "platform-intrinsic" { fn simd_eq(x: T, y: T) -> U; fn simd_reduce_any(x: T) -> bool; + fn simd_reduce_all(x: T) -> bool; fn simd_select(m: M, yes: T, no: T) -> T; } unsafe { @@ -60,6 +86,10 @@ fn simd_intrinsics() { assert!(!simd_reduce_any(i32x4::splat(0))); assert!(simd_reduce_any(i32x4::splat(-1))); + assert!(simd_reduce_any(i32x2::from_array([0, -1]))); + assert!(!simd_reduce_all(i32x4::splat(0))); + 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]));