diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index 547f23f620..f80062668f 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -306,7 +306,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // SIMD operations - "simd_add" | "simd_sub" | "simd_mul" | "simd_div" | "simd_rem" => { + #[rustfmt::skip] + | "simd_add" + | "simd_sub" + | "simd_mul" + | "simd_div" + | "simd_rem" + | "simd_shl" + | "simd_shr" => { 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)?; @@ -321,14 +328,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "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, _ => unreachable!(), }; for i in 0..dest_len { let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?; let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?; - let dest = this.mplace_index(&dest, i)?.into(); - this.binop_ignore_overflow(op, &left, &right, &dest)?; + let dest = this.mplace_index(&dest, i)?; + let (val, overflowed, ty) = this.overflowing_binary_op(op, &left, &right)?; + assert_eq!(ty, dest.layout.ty); + if matches!(op, mir::BinOp::Shl | mir::BinOp::Shr) { + // Shifts have extra UB as SIMD operations that the MIR binop does not have. + // See . + if overflowed { + let r_val = right.to_scalar()?.to_bits(right.layout.size)?; + throw_ub_format!("overflowing shift by {} in `{}` in SIMD lane {}", r_val, intrinsic_name, i); + } + } + this.write_scalar(val, &dest.into())?; } } diff --git a/tests/compile-fail/intrinsics/simd-shl-too-far.rs b/tests/compile-fail/intrinsics/simd-shl-too-far.rs new file mode 100644 index 0000000000..b973386f1b --- /dev/null +++ b/tests/compile-fail/intrinsics/simd-shl-too-far.rs @@ -0,0 +1,15 @@ +#![feature(platform_intrinsics, repr_simd)] + +extern "platform-intrinsic" { + pub(crate) fn simd_shl(x: T, y: T) -> T; +} + +#[repr(simd)] +#[allow(non_camel_case_types)] +struct i32x2(i32, i32); + +fn main() { unsafe { + let x = i32x2(1, 1); + let y = i32x2(100, 0); + simd_shl(x, y); //~ERROR overflowing shift by 100 in `simd_shl` in SIMD lane 0 +} } diff --git a/tests/compile-fail/intrinsics/simd-shr-too-far.rs b/tests/compile-fail/intrinsics/simd-shr-too-far.rs new file mode 100644 index 0000000000..0b4eb8c116 --- /dev/null +++ b/tests/compile-fail/intrinsics/simd-shr-too-far.rs @@ -0,0 +1,15 @@ +#![feature(platform_intrinsics, repr_simd)] + +extern "platform-intrinsic" { + pub(crate) fn simd_shr(x: T, y: T) -> T; +} + +#[repr(simd)] +#[allow(non_camel_case_types)] +struct i32x2(i32, i32); + +fn main() { unsafe { + let x = i32x2(1, 1); + let y = i32x2(20, 40); + simd_shr(x, y); //~ERROR overflowing shift by 40 in `simd_shr` in SIMD lane 1 +} } diff --git a/tests/run-pass/portable-simd.rs b/tests/run-pass/portable-simd.rs index 2d94c87ff0..5d33376618 100644 --- a/tests/run-pass/portable-simd.rs +++ b/tests/run-pass/portable-simd.rs @@ -21,6 +21,8 @@ fn simd_ops_i32() { assert_eq!(a / b, i32x4::from_array([10, 5, 3, 2])); assert_eq!(a / 2, i32x4::splat(5)); assert_eq!(a % b, i32x4::from_array([0, 0, 1, 2])); + assert_eq!(b << 2, i32x4::from_array([4, 8, 12, 16])); + assert_eq!(b >> 1, i32x4::from_array([0, 1, 1, 2])); } fn main() {