Document safety conditions of simd shifts

This commit is contained in:
Alex Crichton 2025-03-10 12:02:57 -07:00 committed by Amanieu d'Antras
parent 28580edab8
commit 267558c5c2

View file

@ -2318,6 +2318,24 @@ pub fn u8x16_narrow_i16x8(a: v128, b: v128) -> v128 {
#[doc(alias("i8x16.shl"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i8x16_shl(a: v128, amt: u32) -> v128 {
// SAFETY: the safety of this intrinsic relies on the fact that the
// shift amount for each lane is less than the number of bits in the input
// lane. In this case the input has 8-bit lanes but the shift amount above
// is `u32`, so a mask is required to discard all the upper bits of `amt` to
// ensure that the safety condition is met.
//
// Note that this is distinct from the behavior of the native WebAssembly
// instruction here where WebAssembly defines this instruction as performing
// a mask as well. This is nonetheless required since this must have defined
// semantics in LLVM, not just WebAssembly.
//
// Finally note that this mask operation is not actually emitted into the
// final binary itself. LLVM understands that the wasm operation implicitly
// masks, so it knows this mask operation is redundant.
//
// Basically the extra mask here is required as a bridge from the documented
// semantics through LLVM back out to WebAssembly. Both ends have the
// documented semantics, and the mask is required by LLVM in the middle.
unsafe { simd_shl(a.as_i8x16(), simd::i8x16::splat((amt & 0x7) as i8)).v128() }
}
@ -2335,6 +2353,8 @@ pub use i8x16_shl as u8x16_shl;
#[doc(alias("i8x16.shr_s"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i8x16_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_i8x16(), simd::i8x16::splat((amt & 0x7) as i8)).v128() }
}
@ -2349,6 +2369,8 @@ pub fn i8x16_shr(a: v128, amt: u32) -> v128 {
#[doc(alias("i8x16.shr_u"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn u8x16_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_u8x16(), simd::u8x16::splat((amt & 0x7) as u8)).v128() }
}
@ -2686,6 +2708,8 @@ pub use i16x8_extend_high_u8x16 as u16x8_extend_high_u8x16;
#[doc(alias("i16x8.shl"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i16x8_shl(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shl(a.as_i16x8(), simd::i16x8::splat((amt & 0xf) as i16)).v128() }
}
@ -2703,6 +2727,8 @@ pub use i16x8_shl as u16x8_shl;
#[doc(alias("i16x8.shr_s"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i16x8_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_i16x8(), simd::i16x8::splat((amt & 0xf) as i16)).v128() }
}
@ -2717,6 +2743,8 @@ pub fn i16x8_shr(a: v128, amt: u32) -> v128 {
#[doc(alias("i16x8.shr_u"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn u16x8_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_u16x8(), simd::u16x8::splat((amt & 0xf) as u16)).v128() }
}
@ -3136,6 +3164,8 @@ pub use i32x4_extend_high_u16x8 as u32x4_extend_high_u16x8;
#[doc(alias("i32x4.shl"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i32x4_shl(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shl(a.as_i32x4(), simd::i32x4::splat((amt & 0x1f) as i32)).v128() }
}
@ -3153,6 +3183,8 @@ pub use i32x4_shl as u32x4_shl;
#[doc(alias("i32x4.shr_s"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i32x4_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_i32x4(), simd::i32x4::splat((amt & 0x1f) as i32)).v128() }
}
@ -3167,6 +3199,8 @@ pub fn i32x4_shr(a: v128, amt: u32) -> v128 {
#[doc(alias("i32x4.shr_u"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn u32x4_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_u32x4(), simd::u32x4::splat(amt & 0x1f)).v128() }
}
@ -3502,6 +3536,8 @@ pub use i64x2_extend_high_u32x4 as u64x2_extend_high_u32x4;
#[doc(alias("i64x2.shl"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i64x2_shl(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shl(a.as_i64x2(), simd::i64x2::splat((amt & 0x3f) as i64)).v128() }
}
@ -3519,6 +3555,8 @@ pub use i64x2_shl as u64x2_shl;
#[doc(alias("i64x2.shr_s"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn i64x2_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_i64x2(), simd::i64x2::splat((amt & 0x3f) as i64)).v128() }
}
@ -3533,6 +3571,8 @@ pub fn i64x2_shr(a: v128, amt: u32) -> v128 {
#[doc(alias("i64x2.shr_u"))]
#[stable(feature = "wasm_simd", since = "1.54.0")]
pub fn u64x2_shr(a: v128, amt: u32) -> v128 {
// SAFETY: see i8x16_shl for more documentation why this is unsafe,
// essentially the shift amount must be valid hence the mask.
unsafe { simd_shr(a.as_u64x2(), simd::u64x2::splat((amt & 0x3f) as u64)).v128() }
}