Skip to content

Commit

Permalink
Fix unintentional UB in SIMD tests
Browse files Browse the repository at this point in the history
  • Loading branch information
saethlin committed Feb 13, 2023
1 parent 8dabf5d commit 9c4696b
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 121 deletions.
146 changes: 42 additions & 104 deletions tests/ui/simd/intrinsic/generic-cast-pass.rs
Original file line number Diff line number Diff line change
@@ -1,121 +1,59 @@
// run-pass
#![allow(unused_must_use)]
// ignore-emscripten FIXME(#45351) hits an LLVM assert

#![feature(repr_simd, platform_intrinsics, concat_idents, test)]
#![allow(non_camel_case_types)]

extern crate test;

#[repr(simd)]
#[derive(PartialEq, Debug)]
struct i32x4(i32, i32, i32, i32);
#[repr(simd)]
#[derive(PartialEq, Debug)]
struct i8x4(i8, i8, i8, i8);

#[repr(simd)]
#[derive(PartialEq, Debug)]
struct u32x4(u32, u32, u32, u32);
#[repr(simd)]
#[derive(PartialEq, Debug)]
struct u8x4(u8, u8, u8, u8);

#[repr(simd)]
#[derive(PartialEq, Debug)]
struct f32x4(f32, f32, f32, f32);

#[repr(simd)]
#[derive(PartialEq, Debug)]
struct f64x4(f64, f64, f64, f64);

#![feature(repr_simd, platform_intrinsics)]

extern "platform-intrinsic" {
fn simd_cast<T, U>(x: T) -> U;
}

const A: i32 = -1234567;
const B: i32 = 12345678;
const C: i32 = -123456789;
const D: i32 = 1234567890;
use std::cmp::{max, min};

trait Foo {
fn is_float() -> bool { false }
fn in_range(x: i32) -> bool;
}
impl Foo for i32 {
fn in_range(_: i32) -> bool { true }
}
impl Foo for i8 {
fn in_range(x: i32) -> bool { -128 <= x && x < 128 }
}
impl Foo for u32 {
fn in_range(x: i32) -> bool { 0 <= x }
}
impl Foo for u8 {
fn in_range(x: i32) -> bool { 0 <= x && x < 128 }
}
impl Foo for f32 {
fn is_float() -> bool { true }
fn in_range(_: i32) -> bool { true }
}
impl Foo for f64 {
fn is_float() -> bool { true }
fn in_range(_: i32) -> bool { true }
}
#[derive(Copy, Clone)]
#[repr(simd)]
struct V<T>([T; 2]);

fn main() {
macro_rules! test {
($from: ident, $to: ident) => {{
// force the casts to actually happen, or else LLVM/rustc
// may fold them and get slightly different results.
let (a, b, c, d) = test::black_box((A as $from, B as $from, C as $from, D as $from));
// the SIMD vectors are all FOOx4, so we can concat_idents
// so we don't have to pass in the extra args to the macro
let mut from = simd_cast(concat_idents!($from, x4)(a, b, c, d));
let mut to = concat_idents!($to, x4)(a as $to,
b as $to,
c as $to,
d as $to);
// assist type inference, it needs to know what `from` is
// for the `if` statements.
to == from;
unsafe {
let u = V::<u32>([i16::MIN as u32, i16::MAX as u32]);
let i: V<i16> = simd_cast(u);
assert_eq!(i.0[0], u.0[0] as i16);
assert_eq!(i.0[1], u.0[1] as i16);
}

// there are platform differences for some out of range
// casts, so we just normalize such things: it's OK for
// "invalid" calculations to result in nonsense answers.
// (e.g., negative float to unsigned integer goes through a
// library routine on the default i686 platforms, and the
// implementation of that routine differs on e.g., Linux
// vs. macOS, resulting in different answers.)
if $from::is_float() {
if !$to::in_range(A) { from.0 = 0 as $to; to.0 = 0 as $to; }
if !$to::in_range(B) { from.1 = 0 as $to; to.1 = 0 as $to; }
if !$to::in_range(C) { from.2 = 0 as $to; to.2 = 0 as $to; }
if !$to::in_range(D) { from.3 = 0 as $to; to.3 = 0 as $to; }
}
unsafe {
let f = V::<f32>([i16::MIN as f32, i16::MAX as f32]);
let i: V<i16> = simd_cast(f);
assert_eq!(i.0[0], f.0[0] as i16);
assert_eq!(i.0[1], f.0[1] as i16);
}

assert!(to == from,
"{} -> {} ({:?} != {:?})", stringify!($from), stringify!($to),
from, to);
}}
unsafe {
let f = V::<f32>([u8::MIN as f32, u8::MAX as f32]);
let u: V<u8> = simd_cast(f);
assert_eq!(u.0[0], f.0[0] as u8);
assert_eq!(u.0[1], f.0[1] as u8);
}
macro_rules! tests {
(: $($to: ident),*) => { () };
// repeating the list twice is easier than writing a cartesian
// product macro
($from: ident $(, $from_: ident)*: $($to: ident),*) => {
fn $from() { unsafe { $( test!($from, $to); )* } }
tests!($($from_),*: $($to),*)
};
($($types: ident),*) => {{
tests!($($types),* : $($types),*);
$($types();)*
}}

unsafe {
// We would like to do isize::MIN..=isize::MAX, but those values are not representable in
// an f64, so we clamp to the range of an i32 to prevent running into UB.
let f = V::<f64>([
max(isize::MIN, i32::MIN as isize) as f64,
min(isize::MAX, i32::MAX as isize) as f64,
]);
let i: V<isize> = simd_cast(f);
assert_eq!(i.0[0], f.0[0] as isize);
assert_eq!(i.0[1], f.0[1] as isize);
}

// test various combinations, including truncation,
// signed/unsigned extension, and floating point casts.
tests!(i32, i8, u32, u8, f32);
tests!(i32, u32, f32, f64)
unsafe {
let f = V::<f64>([
max(usize::MIN, u32::MIN as usize) as f64,
min(usize::MAX, u32::MAX as usize) as f64,
]);
let u: V<usize> = simd_cast(f);
assert_eq!(u.0[0], f.0[0] as usize);
assert_eq!(u.0[1], f.0[1] as usize);
}
}
24 changes: 12 additions & 12 deletions tests/ui/simd/intrinsic/generic-gather-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ fn main() {

// reading from *const
unsafe {
let pointer = &x[0] as *const f32;
let pointer = x.as_ptr();
let pointers = x4(
pointer.offset(0) as *const f32,
pointer.offset(0),
pointer.offset(2),
pointer.offset(4),
pointer.offset(6)
Expand All @@ -39,9 +39,9 @@ fn main() {

// reading from *mut
unsafe {
let pointer = &mut x[0] as *mut f32;
let pointer = x.as_mut_ptr();
let pointers = x4(
pointer.offset(0) as *mut f32,
pointer.offset(0),
pointer.offset(2),
pointer.offset(4),
pointer.offset(6)
Expand All @@ -54,9 +54,9 @@ fn main() {

// writing to *mut
unsafe {
let pointer = &mut x[0] as *mut f32;
let pointer = x.as_mut_ptr();
let pointers = x4(
pointer.offset(0) as *mut f32,
pointer.offset(0),
pointer.offset(2),
pointer.offset(4),
pointer.offset(6)
Expand Down Expand Up @@ -85,9 +85,9 @@ fn main() {

// reading from *const
unsafe {
let pointer = &y[0] as *const *const f32;
let pointer = y.as_ptr();
let pointers = x4(
pointer.offset(0) as *const *const f32,
pointer.offset(0),
pointer.offset(2),
pointer.offset(4),
pointer.offset(6)
Expand All @@ -100,9 +100,9 @@ fn main() {

// reading from *mut
unsafe {
let pointer = &mut y[0] as *mut *const f32;
let pointer = y.as_mut_ptr();
let pointers = x4(
pointer.offset(0) as *mut *const f32,
pointer.offset(0),
pointer.offset(2),
pointer.offset(4),
pointer.offset(6)
Expand All @@ -115,9 +115,9 @@ fn main() {

// writing to *mut
unsafe {
let pointer = &mut y[0] as *mut *const f32;
let pointer = y.as_mut_ptr();
let pointers = x4(
pointer.offset(0) as *mut *const f32,
pointer.offset(0),
pointer.offset(2),
pointer.offset(4),
pointer.offset(6)
Expand Down
11 changes: 6 additions & 5 deletions tests/ui/simd/issue-89193.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ extern "platform-intrinsic" {
fn main() {
let x: [usize; 4] = [10, 11, 12, 13];
let default = x4(0_usize, 1, 2, 3);
let mask = x4(1_i32, 1, 1, 1);
let all_set = u8::MAX as i8; // aka -1
let mask = x4(all_set, all_set, all_set, all_set);
let expected = x4(10_usize, 11, 12, 13);

unsafe {
let pointer = &x[0] as *const usize;
let pointer = x.as_ptr();
let pointers = x4(
pointer.offset(0) as *const usize,
pointer.offset(0),
pointer.offset(1),
pointer.offset(2),
pointer.offset(3)
Expand All @@ -38,9 +39,9 @@ fn main() {
let expected = x4(10_isize, 11, 12, 13);

unsafe {
let pointer = &x[0] as *const isize;
let pointer = x.as_ptr();
let pointers = x4(
pointer.offset(0) as *const isize,
pointer.offset(0),
pointer.offset(1),
pointer.offset(2),
pointer.offset(3)
Expand Down

0 comments on commit 9c4696b

Please sign in to comment.