Skip to content

Commit db217a7

Browse files
authored
cmov: impl Cmov/CmovEq for slices of signed integers (#1373)
Adds impls of both traits for the following: - `[i8]` - `[i16]` - `[i32]` - `[i64]` - `[i128]` The impls cast to the corresponding unsigned integer of the same size, then invoke the corresponding trait impl on that type.
1 parent b39f70f commit db217a7

2 files changed

Lines changed: 107 additions & 1 deletion

File tree

cmov/src/slice.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,107 @@ impl_cmov_with_loop!(
198198
"Implementation for `u128` slices where we can just loop."
199199
);
200200

201+
macro_rules! assert_size_and_alignment_eq {
202+
($signed:ty, $unsigned:ty) => {
203+
const {
204+
assert!(
205+
size_of::<$signed>() == size_of::<$unsigned>(),
206+
"integers are of unequal size"
207+
);
208+
209+
assert!(
210+
align_of::<$signed>() == align_of::<$unsigned>(),
211+
"integers have unequal alignment"
212+
);
213+
}
214+
};
215+
}
216+
217+
/// Implement [`Cmov`] for a signed type by invoking the corresponding unsigned impl.
218+
macro_rules! impl_cmov_for_signed_with_unsigned {
219+
($signed:ty, $unsigned:ty) => {
220+
impl_cmov_for_signed_with_unsigned!(
221+
$signed,
222+
$unsigned,
223+
"Delegating implementation of `Cmov` for signed type which delegates to unsigned."
224+
);
225+
};
226+
($signed:ty, $unsigned:ty, $doc:expr) => {
227+
#[doc = $doc]
228+
#[doc = "# Panics"]
229+
#[doc = "- if slices have unequal lengths"]
230+
impl Cmov for [$signed] {
231+
#[inline]
232+
#[track_caller]
233+
fn cmovnz(&mut self, value: &Self, condition: Condition) {
234+
assert_size_and_alignment_eq!($signed, $unsigned);
235+
236+
// SAFETY:
237+
// - Slices being constructed are of same-sized integers as asserted above.
238+
// - We source the slice length directly from the other valid slice.
239+
#[allow(unsafe_code)]
240+
let (self_unsigned, value_unsigned) = unsafe {
241+
(
242+
slice::from_raw_parts_mut(self.as_mut_ptr() as *mut $unsigned, self.len()),
243+
slice::from_raw_parts(value.as_ptr() as *const $unsigned, value.len()),
244+
)
245+
};
246+
247+
self_unsigned.cmovnz(value_unsigned, condition);
248+
}
249+
}
250+
};
251+
}
252+
253+
/// Implement [`CmovEq`] for a signed type by invoking the corresponding unsigned impl.
254+
macro_rules! impl_cmoveq_for_signed_with_unsigned {
255+
($signed:ty, $unsigned:ty) => {
256+
impl_cmoveq_for_signed_with_unsigned!(
257+
$signed,
258+
$unsigned,
259+
"Delegating implementation of `CmovEq` for signed type which delegates to unsigned."
260+
);
261+
};
262+
($signed:ty, $unsigned:ty, $doc:expr) => {
263+
#[doc = $doc]
264+
#[doc = "# Panics"]
265+
#[doc = "- if slices have unequal lengths"]
266+
impl CmovEq for [$signed] {
267+
#[inline]
268+
fn cmovne(&self, rhs: &Self, input: Condition, output: &mut Condition) {
269+
assert_size_and_alignment_eq!($signed, $unsigned);
270+
271+
// SAFETY:
272+
// - Slices being constructed are of same-sized integers as asserted above.
273+
// - We source the slice length directly from the other valid slice.
274+
#[allow(unsafe_code)]
275+
let (self_unsigned, rhs_unsigned) = unsafe {
276+
(
277+
slice::from_raw_parts(self.as_ptr() as *const $unsigned, self.len()),
278+
slice::from_raw_parts(rhs.as_ptr() as *const $unsigned, rhs.len()),
279+
)
280+
};
281+
282+
self_unsigned.cmovne(rhs_unsigned, input, output);
283+
}
284+
}
285+
};
286+
}
287+
288+
/// Implement [`Cmov`] and [`CmovEq`] for the given signed/unsigned type pair.
289+
macro_rules! impl_cmov_traits_for_signed_with_unsigned {
290+
($signed:ty, $unsigned:ty) => {
291+
impl_cmov_for_signed_with_unsigned!($signed, $unsigned);
292+
impl_cmoveq_for_signed_with_unsigned!($signed, $unsigned);
293+
};
294+
}
295+
296+
impl_cmov_traits_for_signed_with_unsigned!(i8, u8);
297+
impl_cmov_traits_for_signed_with_unsigned!(i16, u16);
298+
impl_cmov_traits_for_signed_with_unsigned!(i32, u32);
299+
impl_cmov_traits_for_signed_with_unsigned!(i64, u64);
300+
impl_cmov_traits_for_signed_with_unsigned!(i128, u128);
301+
201302
/// Optimized implementation for byte slices which coalesces them into word-sized chunks first,
202303
/// then performs [`CmovEq`] at the word-level to cut down on the total number of instructions.
203304
///

cmov/tests/core_impls.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,12 @@ mod slices {
305305
};
306306
}
307307

308-
// TODO: int_slice_test!(i8, i8::MIN, i8::MAX);
308+
int_slice_test!(i8, i8::MIN, i8::MAX);
309+
int_slice_test!(i16, i16::MIN, i16::MAX);
310+
int_slice_test!(i32, i32::MIN, i32::MAX);
311+
int_slice_test!(i64, i64::MIN, i64::MAX);
312+
int_slice_test!(i128, i128::MIN, i128::MAX);
313+
309314
int_slice_test!(u8, 7, u8::MAX);
310315
int_slice_test!(u16, 11, u16::MAX);
311316
int_slice_test!(u32, 13, u32::MAX);

0 commit comments

Comments
 (0)