simd_select_bitmask: the 'padding' bits in the mask are just ignored

This commit is contained in:
Ralf Jung 2025-04-19 11:22:10 +02:00
parent 883f9f72e8
commit 31cb737dee
5 changed files with 15 additions and 45 deletions

View file

@ -577,11 +577,9 @@ pub unsafe fn simd_select<M, T>(mask: M, if_true: T, if_false: T) -> T;
/// For each element, if the bit in `mask` is `1`, select the element from
/// `if_true`. If the corresponding bit in `mask` is `0`, select the element from
/// `if_false`.
/// The remaining bits of the mask are ignored.
///
/// The bitmask bit order matches `simd_bitmask`.
///
/// # Safety
/// Padding bits must be all zero.
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn simd_select_bitmask<M, T>(m: M, yes: T, no: T) -> T;

View file

@ -506,7 +506,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let dest_len = u32::try_from(dest_len).unwrap();
let bitmask_len = u32::try_from(bitmask_len).unwrap();
for i in 0..dest_len {
let bit_i = simd_bitmask_index(i, dest_len, this.data_layout().endian);
let mask = mask & 1u64.strict_shl(bit_i);
@ -517,17 +516,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = if mask != 0 { yes } else { no };
this.write_immediate(*val, &dest)?;
}
for i in dest_len..bitmask_len {
// If the mask is "padded", ensure that padding is all-zero.
// This deliberately does not use `simd_bitmask_index`; these bits are outside
// the bitmask. It does not matter in which order we check them.
let mask = mask & 1u64.strict_shl(i);
if mask != 0 {
throw_ub_format!(
"a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits"
);
}
}
// The remaining bits of the mask are ignored.
}
// Converts a "vector of bool" into a bitmask.
"bitmask" => {

View file

@ -1,15 +0,0 @@
#![feature(core_intrinsics, repr_simd)]
use std::intrinsics::simd::simd_select_bitmask;
#[repr(simd)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
struct i32x2([i32; 2]);
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

@ -1,15 +0,0 @@
error: Undefined Behavior: a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits
--> tests/fail/intrinsics/simd-select-bitmask-invalid.rs:LL:CC
|
LL | simd_select_bitmask(0b11111111u8, x, x);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at tests/fail/intrinsics/simd-select-bitmask-invalid.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -331,6 +331,19 @@ fn simd_mask() {
);
assert_eq!(selected1, i32x4::from_array([0, 0, 0, 1]));
assert_eq!(selected2, selected1);
// Non-zero "padding" (the extra bits) is also allowed.
let selected1 = simd_select_bitmask::<u8, _>(
if cfg!(target_endian = "little") { 0b11111000 } else { 0b11110001 },
i32x4::splat(1), // yes
i32x4::splat(0), // no
);
let selected2 = simd_select_bitmask::<[u8; 1], _>(
if cfg!(target_endian = "little") { [0b11111000] } else { [0b11110001] },
i32x4::splat(1), // yes
i32x4::splat(0), // no
);
assert_eq!(selected1, i32x4::from_array([0, 0, 0, 1]));
assert_eq!(selected2, selected1);
}
// Non-power-of-2 multi-byte mask.