Skip to content

Commit b6d0eec

Browse files
Wrap bitshifts in ops.rs
For all other operators, we use wrapping logic where applicable. This is another case it applies. Per rust-lang/rust#91237, we may wish to specify this as the natural behavior of `simd_{shl,shr}`.
1 parent 81484a3 commit b6d0eec

File tree

1 file changed

+109
-59
lines changed

1 file changed

+109
-59
lines changed

crates/core_simd/src/ops.rs

Lines changed: 109 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,115 @@ where
3232
}
3333
}
3434

35-
/// Checks if the right-hand side argument of a left- or right-shift would cause overflow.
36-
fn invalid_shift_rhs<T>(rhs: T) -> bool
37-
where
38-
T: Default + PartialOrd + core::convert::TryFrom<usize>,
39-
<T as core::convert::TryFrom<usize>>::Error: core::fmt::Debug,
40-
{
41-
let bits_in_type = T::try_from(8 * core::mem::size_of::<T>()).unwrap();
42-
rhs < T::default() || rhs >= bits_in_type
35+
/// SAFETY: This macro should not be used for anything except Shl or Shr, and passed the appropriate shift intrinsic.
36+
/// It handles performing a bitand in addition to calling the shift operator, so that the result
37+
/// is well-defined: LLVM can return a poison value if you shl, lshr, or ashr if rhs >= <Int>::BITS
38+
/// At worst, this will maybe add another instruction and cycle,
39+
/// at best, it may open up more optimization opportunities,
40+
/// or simply be elided entirely, especially for SIMD ISAs which default to this.
41+
///
42+
// FIXME: Consider implementing this in cg_llvm instead?
43+
// cg_clif defaults to this, and scalar MIR shifts also default to wrapping
44+
macro_rules! wrap_bitshift_inner {
45+
(impl<const LANES: usize> $op:ident for Simd<$int:ty, LANES> {
46+
fn $call:ident(self, rhs: Self) -> Self::Output {
47+
unsafe { $simd_call:ident }
48+
}
49+
}) => {
50+
impl<const LANES: usize> $op for Simd<$int, LANES>
51+
where
52+
$int: SimdElement,
53+
LaneCount<LANES>: SupportedLaneCount,
54+
{
55+
type Output = Self;
56+
57+
#[inline]
58+
#[must_use = "operator returns a new vector without mutating the inputs"]
59+
fn $call(self, rhs: Self) -> Self::Output {
60+
unsafe {
61+
$crate::intrinsics::$simd_call(self, rhs.bitand(Simd::splat(<$int>::BITS as $int - 1)))
62+
}
63+
}
64+
}
65+
};
66+
}
67+
68+
macro_rules! wrap_bitshifts {
69+
($(impl<const LANES: usize> ShiftOps for Simd<$int:ty, LANES> {
70+
fn shl(self, rhs: Self) -> Self::Output;
71+
fn shr(self, rhs: Self) -> Self::Output;
72+
})*) => {
73+
$(
74+
wrap_bitshift_inner! {
75+
impl<const LANES: usize> Shl for Simd<$int, LANES> {
76+
fn shl(self, rhs: Self) -> Self::Output {
77+
unsafe { simd_shl }
78+
}
79+
}
80+
}
81+
wrap_bitshift_inner! {
82+
impl<const LANES: usize> Shr for Simd<$int, LANES> {
83+
fn shr(self, rhs: Self) -> Self::Output {
84+
// This automatically monomorphizes to lshr or ashr, depending,
85+
// so it's fine to use it for both UInts and SInts.
86+
unsafe { simd_shr }
87+
}
88+
}
89+
}
90+
)*
91+
};
92+
}
93+
94+
wrap_bitshifts! {
95+
impl<const LANES: usize> ShiftOps for Simd<i8, LANES> {
96+
fn shl(self, rhs: Self) -> Self::Output;
97+
fn shr(self, rhs: Self) -> Self::Output;
98+
}
99+
100+
impl<const LANES: usize> ShiftOps for Simd<i16, LANES> {
101+
fn shl(self, rhs: Self) -> Self::Output;
102+
fn shr(self, rhs: Self) -> Self::Output;
103+
}
104+
105+
impl<const LANES: usize> ShiftOps for Simd<i32, LANES> {
106+
fn shl(self, rhs: Self) -> Self::Output;
107+
fn shr(self, rhs: Self) -> Self::Output;
108+
}
109+
110+
impl<const LANES: usize> ShiftOps for Simd<i64, LANES> {
111+
fn shl(self, rhs: Self) -> Self::Output;
112+
fn shr(self, rhs: Self) -> Self::Output;
113+
}
114+
115+
impl<const LANES: usize> ShiftOps for Simd<isize, LANES> {
116+
fn shl(self, rhs: Self) -> Self::Output;
117+
fn shr(self, rhs: Self) -> Self::Output;
118+
}
119+
120+
impl<const LANES: usize> ShiftOps for Simd<u8, LANES> {
121+
fn shl(self, rhs: Self) -> Self::Output;
122+
fn shr(self, rhs: Self) -> Self::Output;
123+
}
124+
125+
impl<const LANES: usize> ShiftOps for Simd<u16, LANES> {
126+
fn shl(self, rhs: Self) -> Self::Output;
127+
fn shr(self, rhs: Self) -> Self::Output;
128+
}
129+
130+
impl<const LANES: usize> ShiftOps for Simd<u32, LANES> {
131+
fn shl(self, rhs: Self) -> Self::Output;
132+
fn shr(self, rhs: Self) -> Self::Output;
133+
}
134+
135+
impl<const LANES: usize> ShiftOps for Simd<u64, LANES> {
136+
fn shl(self, rhs: Self) -> Self::Output;
137+
fn shr(self, rhs: Self) -> Self::Output;
138+
}
139+
140+
impl<const LANES: usize> ShiftOps for Simd<usize, LANES> {
141+
fn shl(self, rhs: Self) -> Self::Output;
142+
fn shr(self, rhs: Self) -> Self::Output;
143+
}
43144
}
44145

45146
/// Automatically implements operators over references in addition to the provided operator.
@@ -85,12 +186,6 @@ macro_rules! impl_op {
85186
{ impl Rem for $scalar:ty } => {
86187
impl_op! { @binary $scalar, Rem::rem, simd_rem }
87188
};
88-
{ impl Shl for $scalar:ty } => {
89-
impl_op! { @binary $scalar, Shl::shl, simd_shl }
90-
};
91-
{ impl Shr for $scalar:ty } => {
92-
impl_op! { @binary $scalar, Shr::shr, simd_shr }
93-
};
94189
{ impl BitAnd for $scalar:ty } => {
95190
impl_op! { @binary $scalar, BitAnd::bitand, simd_and }
96191
};
@@ -202,51 +297,6 @@ macro_rules! impl_unsigned_int_ops {
202297
}
203298
}
204299
}
205-
206-
// shifts panic on overflow
207-
impl_ref_ops! {
208-
impl<const LANES: usize> core::ops::Shl<Self> for Simd<$scalar, LANES>
209-
where
210-
LaneCount<LANES>: SupportedLaneCount,
211-
{
212-
type Output = Self;
213-
214-
#[inline]
215-
fn shl(self, rhs: Self) -> Self::Output {
216-
// TODO there is probably a better way of doing this
217-
if rhs.as_array()
218-
.iter()
219-
.copied()
220-
.any(invalid_shift_rhs)
221-
{
222-
panic!("attempt to shift left with overflow");
223-
}
224-
unsafe { intrinsics::simd_shl(self, rhs) }
225-
}
226-
}
227-
}
228-
229-
impl_ref_ops! {
230-
impl<const LANES: usize> core::ops::Shr<Self> for Simd<$scalar, LANES>
231-
where
232-
LaneCount<LANES>: SupportedLaneCount,
233-
{
234-
type Output = Self;
235-
236-
#[inline]
237-
fn shr(self, rhs: Self) -> Self::Output {
238-
// TODO there is probably a better way of doing this
239-
if rhs.as_array()
240-
.iter()
241-
.copied()
242-
.any(invalid_shift_rhs)
243-
{
244-
panic!("attempt to shift with overflow");
245-
}
246-
unsafe { intrinsics::simd_shr(self, rhs) }
247-
}
248-
}
249-
}
250300
)*
251301
};
252302
}

0 commit comments

Comments
 (0)