Skip to content

Commit 9b22e90

Browse files
sayantnfolkertdev
andcommitted
Add funnel_sh{l,r} functions and intrinsics
- Add a fallback implementation for the intrinsics Co-Authored-By: folkertdev <[email protected]>
1 parent f5703d5 commit 9b22e90

File tree

6 files changed

+207
-0
lines changed

6 files changed

+207
-0
lines changed

library/core/src/intrinsics/fallback.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,58 @@ impl_disjoint_bitor! {
148148
u8, u16, u32, u64, u128, usize,
149149
i8, i16, i32, i64, i128, isize,
150150
}
151+
152+
#[const_trait]
153+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
154+
pub trait FunnelShift: Copy + 'static {
155+
/// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle
156+
/// different types since calling intrinsics with generics doesn't work.
157+
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self;
158+
159+
/// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle
160+
/// different types since calling intrinsics with generics doesn't work.
161+
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self;
162+
}
163+
164+
macro_rules! impl_funnel_shifts {
165+
($($type:ident),*) => {$(
166+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
167+
impl const FunnelShift for $type {
168+
#[cfg_attr(miri, track_caller)]
169+
#[inline]
170+
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self {
171+
if shift == 0 {
172+
self
173+
} else {
174+
// SAFETY: our precondition is that `shift < T::BITS`, so these are valid
175+
unsafe {
176+
super::disjoint_bitor(
177+
super::unchecked_shl(self, shift),
178+
super::unchecked_shr(rhs, $type::BITS - shift),
179+
)
180+
}
181+
}
182+
}
183+
184+
#[cfg_attr(miri, track_caller)]
185+
#[inline]
186+
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self {
187+
if shift == 0 {
188+
rhs
189+
} else {
190+
// SAFETY: our precondition is that `shift < T::BITS`, so these are valid
191+
unsafe {
192+
super::disjoint_bitor(
193+
super::unchecked_shl(self, $type::BITS - shift),
194+
super::unchecked_shr(rhs, shift),
195+
)
196+
}
197+
}
198+
}
199+
}
200+
)*};
201+
}
202+
203+
impl_funnel_shifts! {
204+
u8, u16, u32, u64, u128, usize
205+
}

library/core/src/intrinsics/mod.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,59 @@ pub const fn saturating_add<T: Copy>(a: T, b: T) -> T;
21022102
#[rustc_intrinsic]
21032103
pub const fn saturating_sub<T: Copy>(a: T, b: T) -> T;
21042104

2105+
/// Funnel Shift left.
2106+
///
2107+
/// Concatenates `a` and `b` (with `a` in the most significant half),
2108+
/// creating an integer twice as wide. Then shift this integer left
2109+
/// by `shift`), and extract the most significant half. If `a` and `b`
2110+
/// are the same, this is equivalent to a rotate left operation.
2111+
///
2112+
/// It is undefined behavior if `shift` is greater than or equal to the
2113+
/// bit size of `T`.
2114+
///
2115+
/// Safe versions of this intrinsic are available on the integer primitives
2116+
/// via the `funnel_shl` method. For example, [`u32::funnel_shl`].
2117+
#[rustc_intrinsic]
2118+
#[rustc_nounwind]
2119+
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
2120+
#[unstable(feature = "funnel_shifts", issue = "145686")]
2121+
#[miri::intrinsic_fallback_is_spec]
2122+
pub const unsafe fn unchecked_funnel_shl<T: [const] fallback::FunnelShift>(
2123+
a: T,
2124+
b: T,
2125+
shift: u32,
2126+
) -> T {
2127+
// SAFETY: caller ensures that `shift` is in-range
2128+
unsafe { a.unchecked_funnel_shl(b, shift) }
2129+
}
2130+
2131+
/// Funnel Shift right.
2132+
///
2133+
/// Concatenates `a` and `b` (with `a` in the most significant half),
2134+
/// creating an integer twice as wide. Then shift this integer right
2135+
/// by `shift` (taken modulo the bit size of `T`), and extract the
2136+
/// least significant half. If `a` and `b` are the same, this is equivalent
2137+
/// to a rotate right operation.
2138+
///
2139+
/// It is undefined behavior if `shift` is greater than or equal to the
2140+
/// bit size of `T`.
2141+
///
2142+
/// Safer versions of this intrinsic are available on the integer primitives
2143+
/// via the `funnel_shr` method. For example, [`u32::funnel_shr`]
2144+
#[rustc_intrinsic]
2145+
#[rustc_nounwind]
2146+
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
2147+
#[unstable(feature = "funnel_shifts", issue = "145686")]
2148+
#[miri::intrinsic_fallback_is_spec]
2149+
pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>(
2150+
a: T,
2151+
b: T,
2152+
shift: u32,
2153+
) -> T {
2154+
// SAFETY: caller ensures that `shift` is in-range
2155+
unsafe { a.unchecked_funnel_shr(b, shift) }
2156+
}
2157+
21052158
/// This is an implementation detail of [`crate::ptr::read`] and should
21062159
/// not be used anywhere else. See its comments for why this exists.
21072160
///

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156
#![feature(f128)]
157157
#![feature(freeze_impls)]
158158
#![feature(fundamental)]
159+
#![feature(funnel_shifts)]
159160
#![feature(if_let_guard)]
160161
#![feature(intra_doc_pointers)]
161162
#![feature(intrinsics)]

library/core/src/num/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,9 @@ impl u8 {
454454
rot = 2,
455455
rot_op = "0x82",
456456
rot_result = "0xa",
457+
fsh_op = "0x36",
458+
fshl_result = "0x8",
459+
fshr_result = "0x8d",
457460
swap_op = "0x12",
458461
swapped = "0x12",
459462
reversed = "0x48",
@@ -1088,6 +1091,9 @@ impl u16 {
10881091
rot = 4,
10891092
rot_op = "0xa003",
10901093
rot_result = "0x3a",
1094+
fsh_op = "0x2de",
1095+
fshl_result = "0x30",
1096+
fshr_result = "0x302d",
10911097
swap_op = "0x1234",
10921098
swapped = "0x3412",
10931099
reversed = "0x2c48",
@@ -1135,6 +1141,9 @@ impl u32 {
11351141
rot = 8,
11361142
rot_op = "0x10000b3",
11371143
rot_result = "0xb301",
1144+
fsh_op = "0x2fe78e45",
1145+
fshl_result = "0xb32f",
1146+
fshr_result = "0xb32fe78e",
11381147
swap_op = "0x12345678",
11391148
swapped = "0x78563412",
11401149
reversed = "0x1e6a2c48",
@@ -1158,6 +1167,9 @@ impl u64 {
11581167
rot = 12,
11591168
rot_op = "0xaa00000000006e1",
11601169
rot_result = "0x6e10aa",
1170+
fsh_op = "0x2fe78e45983acd98",
1171+
fshl_result = "0x6e12fe",
1172+
fshr_result = "0x6e12fe78e45983ac",
11611173
swap_op = "0x1234567890123456",
11621174
swapped = "0x5634129078563412",
11631175
reversed = "0x6a2c48091e6a2c48",
@@ -1181,6 +1193,9 @@ impl u128 {
11811193
rot = 16,
11821194
rot_op = "0x13f40000000000000000000000004f76",
11831195
rot_result = "0x4f7613f4",
1196+
fsh_op = "0x2fe78e45983acd98039000008736273",
1197+
fshl_result = "0x4f7602fe",
1198+
fshr_result = "0x4f7602fe78e45983acd9803900000873",
11841199
swap_op = "0x12345678901234567890123456789012",
11851200
swapped = "0x12907856341290785634129078563412",
11861201
reversed = "0x48091e6a2c48091e6a2c48091e6a2c48",
@@ -1207,6 +1222,9 @@ impl usize {
12071222
rot = 4,
12081223
rot_op = "0xa003",
12091224
rot_result = "0x3a",
1225+
fsh_op = "0x2fe78e45983acd98039000008736273",
1226+
fshl_result = "0x4f7602fe",
1227+
fshr_result = "0x4f7602fe78e45983acd9803900000873",
12101228
swap_op = "0x1234",
12111229
swapped = "0x3412",
12121230
reversed = "0x2c48",
@@ -1231,6 +1249,9 @@ impl usize {
12311249
rot = 8,
12321250
rot_op = "0x10000b3",
12331251
rot_result = "0xb301",
1252+
fsh_op = "0x2fe78e45",
1253+
fshl_result = "0xb32f",
1254+
fshr_result = "0xb32fe78e",
12341255
swap_op = "0x12345678",
12351256
swapped = "0x78563412",
12361257
reversed = "0x1e6a2c48",
@@ -1255,6 +1276,9 @@ impl usize {
12551276
rot = 12,
12561277
rot_op = "0xaa00000000006e1",
12571278
rot_result = "0x6e10aa",
1279+
fsh_op = "0x2fe78e45983acd98",
1280+
fshl_result = "0x6e12fe",
1281+
fshr_result = "0x6e12fe78e45983ac",
12581282
swap_op = "0x1234567890123456",
12591283
swapped = "0x5634129078563412",
12601284
reversed = "0x6a2c48091e6a2c48",

library/core/src/num/uint_macros.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ macro_rules! uint_impl {
1414
rot = $rot:literal,
1515
rot_op = $rot_op:literal,
1616
rot_result = $rot_result:literal,
17+
fsh_op = $fsh_op:literal,
18+
fshl_result = $fshl_result:literal,
19+
fshr_result = $fshr_result:literal,
1720
swap_op = $swap_op:literal,
1821
swapped = $swapped:literal,
1922
reversed = $reversed:literal,
@@ -375,6 +378,76 @@ macro_rules! uint_impl {
375378
return intrinsics::rotate_right(self, n);
376379
}
377380

381+
/// Performs a left funnel shift (concatenates `self` with `rhs`, with `self`
382+
/// making up the most significant half, then shifts the combined value left
383+
/// by `n`, and most significant half is extracted to produce the result).
384+
///
385+
/// Please note this isn't the same operation as the `<<` shifting operator or
386+
/// [`rotate_left`](Self::rotate_left), although `a.funnel_shl(a, n)` is *equivalent*
387+
/// to `a.rotate_left(n)`.
388+
///
389+
/// # Panics
390+
///
391+
/// If `n` is greater than or equal to the number of bits in `self`
392+
///
393+
/// # Examples
394+
///
395+
/// Basic usage:
396+
///
397+
/// ```
398+
/// #![feature(funnel_shifts)]
399+
#[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")]
400+
#[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")]
401+
#[doc = concat!("let m = ", $fshl_result, ";")]
402+
///
403+
#[doc = concat!("assert_eq!(a.funnel_shl(b, ", $rot, "), m);")]
404+
/// ```
405+
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
406+
#[unstable(feature = "funnel_shifts", issue = "145686")]
407+
#[must_use = "this returns the result of the operation, \
408+
without modifying the original"]
409+
#[inline(always)]
410+
pub const fn funnel_shl(self, rhs: Self, n: u32) -> Self {
411+
assert!(n < Self::BITS, "attempt to funnel shift left with overflow");
412+
// SAFETY: just checked that `shift` is in-range
413+
unsafe { intrinsics::unchecked_funnel_shl(self, rhs, n) }
414+
}
415+
416+
/// Performs a right funnel shift (concatenates `self` and `rhs`, with `self`
417+
/// making up the most significant half, then shifts the combined value right
418+
/// by `n`, and least significant half is extracted to produce the result).
419+
///
420+
/// Please note this isn't the same operation as the `>>` shifting operator or
421+
/// [`rotate_right`](Self::rotate_right), although `a.funnel_shr(a, n)` is *equivalent*
422+
/// to `a.rotate_right(n)`.
423+
///
424+
/// # Panics
425+
///
426+
/// If `n` is greater than or equal to the number of bits in `self`
427+
///
428+
/// # Examples
429+
///
430+
/// Basic usage:
431+
///
432+
/// ```
433+
/// #![feature(funnel_shifts)]
434+
#[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")]
435+
#[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")]
436+
#[doc = concat!("let m = ", $fshr_result, ";")]
437+
///
438+
#[doc = concat!("assert_eq!(a.funnel_shr(b, ", $rot, "), m);")]
439+
/// ```
440+
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
441+
#[unstable(feature = "funnel_shifts", issue = "145686")]
442+
#[must_use = "this returns the result of the operation, \
443+
without modifying the original"]
444+
#[inline(always)]
445+
pub const fn funnel_shr(self, rhs: Self, n: u32) -> Self {
446+
assert!(n < Self::BITS, "attempt to funnel shift right with overflow");
447+
// SAFETY: just checked that `shift` is in-range
448+
unsafe { intrinsics::unchecked_funnel_shr(self, rhs, n) }
449+
}
450+
378451
/// Reverses the byte order of the integer.
379452
///
380453
/// # Examples

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@
296296
#![feature(f128)]
297297
#![feature(ffi_const)]
298298
#![feature(formatting_options)]
299+
#![feature(funnel_shifts)]
299300
#![feature(hash_map_internals)]
300301
#![feature(hash_map_macro)]
301302
#![feature(if_let_guard)]

0 commit comments

Comments
 (0)