From a534bbbf8aeb14d2ee1c2d167e6e6073a114bf69 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Nov 2021 17:13:16 -0500 Subject: [PATCH 1/2] portable SIMD: add rem intrinsic; test div and rem intrinsic UB --- src/shims/intrinsics.rs | 3 ++- .../{div-by-zero-1.rs => div-by-zero.rs} | 0 .../{div-by-zero-2.rs => rem-by-zero.rs} | 0 tests/compile-fail/intrinsics/simd-div-by-zero.rs | 15 +++++++++++++++ tests/compile-fail/intrinsics/simd-rem-by-zero.rs | 15 +++++++++++++++ tests/run-pass/portable-simd.rs | 2 ++ 6 files changed, 34 insertions(+), 1 deletion(-) rename tests/compile-fail/intrinsics/{div-by-zero-1.rs => div-by-zero.rs} (100%) rename tests/compile-fail/intrinsics/{div-by-zero-2.rs => rem-by-zero.rs} (100%) create mode 100644 tests/compile-fail/intrinsics/simd-div-by-zero.rs create mode 100644 tests/compile-fail/intrinsics/simd-rem-by-zero.rs diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index d684b41ed81..547f23f620d 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -306,7 +306,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // SIMD operations - "simd_add" | "simd_sub" | "simd_mul" | "simd_div" => { + "simd_add" | "simd_sub" | "simd_mul" | "simd_div" | "simd_rem" => { 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)?; @@ -320,6 +320,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "simd_sub" => mir::BinOp::Sub, "simd_mul" => mir::BinOp::Mul, "simd_div" => mir::BinOp::Div, + "simd_rem" => mir::BinOp::Rem, _ => unreachable!(), }; diff --git a/tests/compile-fail/intrinsics/div-by-zero-1.rs b/tests/compile-fail/intrinsics/div-by-zero.rs similarity index 100% rename from tests/compile-fail/intrinsics/div-by-zero-1.rs rename to tests/compile-fail/intrinsics/div-by-zero.rs diff --git a/tests/compile-fail/intrinsics/div-by-zero-2.rs b/tests/compile-fail/intrinsics/rem-by-zero.rs similarity index 100% rename from tests/compile-fail/intrinsics/div-by-zero-2.rs rename to tests/compile-fail/intrinsics/rem-by-zero.rs diff --git a/tests/compile-fail/intrinsics/simd-div-by-zero.rs b/tests/compile-fail/intrinsics/simd-div-by-zero.rs new file mode 100644 index 00000000000..4244e63d23e --- /dev/null +++ b/tests/compile-fail/intrinsics/simd-div-by-zero.rs @@ -0,0 +1,15 @@ +#![feature(platform_intrinsics, repr_simd)] + +extern "platform-intrinsic" { + pub(crate) fn simd_div(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(1, 0); + simd_div(x, y); //~ERROR Undefined Behavior: dividing by zero +} } diff --git a/tests/compile-fail/intrinsics/simd-rem-by-zero.rs b/tests/compile-fail/intrinsics/simd-rem-by-zero.rs new file mode 100644 index 00000000000..bc3128b5fb5 --- /dev/null +++ b/tests/compile-fail/intrinsics/simd-rem-by-zero.rs @@ -0,0 +1,15 @@ +#![feature(platform_intrinsics, repr_simd)] + +extern "platform-intrinsic" { + pub(crate) fn simd_rem(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(1, 0); + simd_rem(x, y); //~ERROR Undefined Behavior: calculating the remainder with a divisor of zero +} } diff --git a/tests/run-pass/portable-simd.rs b/tests/run-pass/portable-simd.rs index 42a6befd868..2d94c87ff04 100644 --- a/tests/run-pass/portable-simd.rs +++ b/tests/run-pass/portable-simd.rs @@ -9,6 +9,7 @@ fn simd_ops_f32() { assert_eq!(a * b, f32x4::from_array([10.0, 20.0, 30.0, 40.0])); assert_eq!(b / a, f32x4::from_array([0.1, 0.2, 0.3, 0.4])); assert_eq!(a / 2.0, f32x4::splat(5.0)); + assert_eq!(a % b, f32x4::from_array([0.0, 0.0, 1.0, 2.0])); } fn simd_ops_i32() { @@ -19,6 +20,7 @@ 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 / 2, i32x4::splat(5)); + assert_eq!(a % b, i32x4::from_array([0, 0, 1, 2])); } fn main() { From 4414d963233ab37f2c3941e7b5dde043a77d37f8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Nov 2021 17:24:47 -0500 Subject: [PATCH 2/2] implement shl and shr SIMD intrinsics --- src/shims/intrinsics.rs | 25 ++++++++++++++++--- .../intrinsics/simd-shl-too-far.rs | 15 +++++++++++ .../intrinsics/simd-shr-too-far.rs | 15 +++++++++++ tests/run-pass/portable-simd.rs | 2 ++ 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/compile-fail/intrinsics/simd-shl-too-far.rs create mode 100644 tests/compile-fail/intrinsics/simd-shr-too-far.rs diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index 547f23f620d..f80062668f3 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 00000000000..b973386f1b5 --- /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 00000000000..0b4eb8c1167 --- /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 2d94c87ff04..5d333766187 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() {