diff --git a/rust-version b/rust-version index ba41809f07..cab2da408d 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -6a705566166debf5eff88c57140df607fa409aaa +f0c4da49983aa699f715caf681e3154b445fb60b diff --git a/src/helpers.rs b/src/helpers.rs index 0c4e0c4e96..2f1c74a058 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -758,3 +758,18 @@ pub fn immty_from_uint_checked<'tcx>( err_unsup_format!("unsigned value {:#x} does not fit in {} bits", int, layout.size.bits()) })?) } + +pub fn bool_to_simd_element(b: bool, size: Size) -> Scalar { + // SIMD uses all-1 as pattern for "true" + let val = if b { -1 } else { 0 }; + Scalar::from_int(val, size) +} + +pub fn simd_element_to_bool<'tcx>(elem: ImmTy<'tcx, Tag>) -> InterpResult<'tcx, bool> { + let val = elem.to_scalar()?.to_int(elem.layout.size)?; + Ok(match val { + 0 => false, + -1 => true, + _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), + }) +} diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index a75367b82c..e849233141 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -8,7 +8,7 @@ use rustc_middle::{mir, mir::BinOp, ty, ty::FloatTy}; use rustc_target::abi::{Align, Integer}; use crate::*; -use helpers::check_arg_count; +use helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool}; pub enum AtomicOp { MirOp(mir::BinOp, bool), @@ -365,8 +365,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Special handling for boolean-returning operations assert_eq!(ty, this.tcx.types.bool); let val = val.to_bool().unwrap(); - let val = if val { -1 } else { 0 }; // SIMD uses all-1 as pattern for "true" - let val = Scalar::from_int(val, dest.layout.size); + let val = bool_to_simd_element(val, dest.layout.size); this.write_scalar(val, &dest.into())?; } else { assert_eq!(ty, dest.layout.ty); @@ -381,21 +380,34 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let mut res = false; // the neutral element for i in 0..arg_len { let op = this.read_immediate(&this.mplace_index(&arg, i)?.into())?; - // We convert it to a *signed* integer and expect either 0 or -1 (the latter means all bits were set). - let val = op.to_scalar()?.to_int(op.layout.size)?; - let val = match val { - 0 => false, - -1 => true, - _ => - throw_ub_format!( - "each element of a simd_reduce_any operand must be all-0-bits or all-1-bits" - ), - }; + let val = simd_element_to_bool(op)?; res = res | val; } this.write_scalar(Scalar::from_bool(res), dest)?; } + "simd_select" => { + let &[ref mask, ref yes, ref no] = check_arg_count(args)?; + let (mask, mask_len) = this.operand_to_simd(mask)?; + let (yes, yes_len) = this.operand_to_simd(yes)?; + let (no, no_len) = this.operand_to_simd(no)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, mask_len); + assert_eq!(dest_len, yes_len); + assert_eq!(dest_len, no_len); + + for i in 0..dest_len { + let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; + let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; + let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + + let mask = simd_element_to_bool(mask)?; + let val = if mask { yes } else { no }; + this.write_immediate(*val, &dest.into())?; + } + } // Atomic operations "atomic_load" => this.atomic_load(args, dest, AtomicReadOp::SeqCst)?, diff --git a/tests/run-pass/portable-simd.rs b/tests/run-pass/portable-simd.rs index 17fea59166..98d5b65e3e 100644 --- a/tests/run-pass/portable-simd.rs +++ b/tests/run-pass/portable-simd.rs @@ -20,7 +20,9 @@ fn simd_ops_i32() { 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([0, 2, 2, 0])); @@ -29,19 +31,22 @@ fn simd_ops_i32() { fn simd_intrinsics() { extern "platform-intrinsic" { - pub(crate) fn simd_eq(x: T, y: T) -> U; - pub(crate) fn simd_reduce_any(x: T) -> bool; + fn simd_eq(x: T, y: T) -> U; + fn simd_reduce_any(x: T) -> bool; + fn simd_select(m: M, yes: T, no: T) -> T; } - - // Make sure simd_eq returns all-1 for `true` - let a = i32x4::splat(10); - let b = i32x4::from_array([1, 2, 10, 4]); - let c: i32x4 = unsafe { simd_eq(a, b) }; - assert_eq!(c, i32x4::from_array([0, 0, -1, 0])); - unsafe { + // Make sure simd_eq returns all-1 for `true` + let a = i32x4::splat(10); + let b = i32x4::from_array([1, 2, 10, 4]); + let c: i32x4 = simd_eq(a, b); + assert_eq!(c, i32x4::from_array([0, 0, -1, 0])); + assert!(!simd_reduce_any(i32x4::splat(0))); assert!(simd_reduce_any(i32x4::splat(-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])); } }