mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-16 17:03:35 +00:00
Rollup merge of #107973 - saethlin:fix-simd-test-ub, r=workingjubilee
Fix unintentional UB in SIMD tests r? `@workingjubilee`
This commit is contained in:
commit
4bf58083ac
@ -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;
|
||||
|
||||
// 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; }
|
||||
}
|
||||
|
||||
assert!(to == from,
|
||||
"{} -> {} ({:?} != {:?})", stringify!($from), stringify!($to),
|
||||
from, to);
|
||||
}}
|
||||
}
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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::<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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,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)
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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)
|
||||
|
@ -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)
|
||||
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user