detect when unused bits of a SIMD bitmask are non-0

This commit is contained in:
Ralf Jung 2022-03-16 20:52:01 -04:00
parent bfed3c4f0d
commit b5d3a25b49
3 changed files with 51 additions and 9 deletions

View File

@ -669,10 +669,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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)?;
let bitmask_len = dest_len.max(8);
assert!(mask.layout.ty.is_integral());
assert_eq!(dest_len.max(8), mask.layout.size.bits());
assert!(dest_len <= 64);
assert!(bitmask_len <= 64);
assert_eq!(bitmask_len, mask.layout.size.bits());
assert_eq!(dest_len, yes_len);
assert_eq!(dest_len, no_len);
@ -684,7 +685,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
.unwrap();
for i in 0..dest_len {
let mask =
mask & (1 << simd_bitmask_index(i, dest_len, this.data_layout().endian));
mask & (1 << simd_bitmask_index(i, bitmask_len, this.data_layout().endian));
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)?;
@ -692,6 +693,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
let val = if mask != 0 { yes } else { no };
this.write_immediate(*val, &dest.into())?;
}
for i in dest_len..bitmask_len {
// If the mask is "padded", ensure that padding is all-zero.
let mask =
mask & (1 << simd_bitmask_index(i, bitmask_len, this.data_layout().endian));
if mask != 0 {
throw_ub_format!(
"a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits"
);
}
}
}
#[rustfmt::skip]
"simd_cast" | "simd_as" => {
@ -820,16 +831,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
"simd_bitmask" => {
let &[ref op] = check_arg_count(args)?;
let (op, op_len) = this.operand_to_simd(op)?;
let bitmask_len = op_len.max(8);
assert!(dest.layout.ty.is_integral());
assert_eq!(op_len.max(8), dest.layout.size.bits());
assert!(op_len <= 64);
assert!(bitmask_len <= 64);
assert_eq!(bitmask_len, dest.layout.size.bits());
let mut res = 0u64;
for i in 0..op_len {
let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?;
if simd_element_to_bool(op)? {
res |= 1 << simd_bitmask_index(i, op_len, this.data_layout().endian);
res |= 1 << simd_bitmask_index(i, bitmask_len, this.data_layout().endian);
}
}
this.write_int(res, dest)?;
@ -1370,10 +1382,10 @@ fn simd_element_to_bool<'tcx>(elem: ImmTy<'tcx, Tag>) -> InterpResult<'tcx, bool
})
}
fn simd_bitmask_index(idx: u64, len: u64, endianess: Endian) -> u64 {
assert!(idx < len);
fn simd_bitmask_index(idx: u64, bitmask_len: u64, endianess: Endian) -> u64 {
assert!(idx < bitmask_len);
match endianess {
Endian::Little => idx,
Endian::Big => len.max(8) - 1 - idx, // reverse order of bits
Endian::Big => bitmask_len - 1 - idx, // reverse order of bits
}
}

View File

@ -0,0 +1,15 @@
#![feature(platform_intrinsics, repr_simd)]
extern "platform-intrinsic" {
fn simd_select_bitmask<M, T>(m: M, yes: T, no: T) -> T;
}
#[repr(simd)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
struct i32x2(i32, i32);
fn main() { unsafe {
let x = i32x2(0, 1);
simd_select_bitmask(0b11111111u8, x, x); //~ERROR bitmask less than 8 bits long must be filled with 0s for the remaining bits
} }

View File

@ -0,0 +1,15 @@
#![feature(platform_intrinsics, repr_simd)]
extern "platform-intrinsic" {
fn simd_select<M, T>(m: M, yes: T, no: T) -> T;
}
#[repr(simd)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
struct i32x2(i32, i32);
fn main() { unsafe {
let x = i32x2(0, 1);
simd_select(x, x, x); //~ERROR must be all-0-bits or all-1-bits
} }