Skip to content

Commit e3da4c8

Browse files
authored
Rollup merge of rust-lang#145690 - sayantn:integer-funnel-shift, r=tgross35
Implement Integer funnel shifts Tracking issue: rust-lang#145686 ACP: rust-lang/libs-team#642 This implements funnel shifts on primitive integer types. Implements this for cg_llvm, with a fallback impl for everything else Thanks ``@folkertdev`` for the fixes and tests cc ``@rust-lang/libs-api``
2 parents 786289a + 62b4347 commit e3da4c8

File tree

16 files changed

+338
-9
lines changed

16 files changed

+338
-9
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
383383
| sym::rotate_left
384384
| sym::rotate_right
385385
| sym::saturating_add
386-
| sym::saturating_sub => {
386+
| sym::saturating_sub
387+
| sym::unchecked_funnel_shl
388+
| sym::unchecked_funnel_shr => {
387389
let ty = args[0].layout.ty;
388390
if !ty.is_integral() {
389391
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
@@ -424,18 +426,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
424426
sym::bitreverse => {
425427
self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])
426428
}
427-
sym::rotate_left | sym::rotate_right => {
428-
let is_left = name == sym::rotate_left;
429-
let val = args[0].immediate();
430-
let raw_shift = args[1].immediate();
431-
// rotate = funnel shift with first two args the same
429+
sym::rotate_left
430+
| sym::rotate_right
431+
| sym::unchecked_funnel_shl
432+
| sym::unchecked_funnel_shr => {
433+
let is_left = name == sym::rotate_left || name == sym::unchecked_funnel_shl;
434+
let lhs = args[0].immediate();
435+
let (rhs, raw_shift) =
436+
if name == sym::rotate_left || name == sym::rotate_right {
437+
// rotate = funnel shift with first two args the same
438+
(lhs, args[1].immediate())
439+
} else {
440+
(args[1].immediate(), args[2].immediate())
441+
};
432442
let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' });
433443

434444
// llvm expects shift to be the same type as the values, but rust
435445
// always uses `u32`.
436-
let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
446+
let raw_shift = self.intcast(raw_shift, self.val_ty(lhs), false);
437447

438-
self.call_intrinsic(llvm_name, &[llty], &[val, val, raw_shift])
448+
self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs, raw_shift])
439449
}
440450
sym::saturating_add | sym::saturating_sub => {
441451
let is_add = name == sym::saturating_add;

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,9 @@ pub(crate) fn check_intrinsic_type(
449449
}
450450
sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)),
451451
sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), tcx.types.u32], param(0)),
452+
sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => {
453+
(1, 0, vec![param(0), param(0), tcx.types.u32], param(0))
454+
}
452455
sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => {
453456
(1, 0, vec![param(0), param(0)], param(0))
454457
}

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2281,6 +2281,8 @@ symbols! {
22812281
unboxed_closures,
22822282
unchecked_add,
22832283
unchecked_div,
2284+
unchecked_funnel_shl,
2285+
unchecked_funnel_shr,
22842286
unchecked_mul,
22852287
unchecked_rem,
22862288
unchecked_shl,

library/core/src/intrinsics/fallback.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,76 @@ 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+
// This implementation is also used by Miri so we have to check the precondition.
172+
// SAFETY: this is guaranteed by the caller
173+
unsafe { super::assume(shift < $type::BITS) };
174+
if shift == 0 {
175+
self
176+
} else {
177+
// SAFETY:
178+
// - `shift < T::BITS`, which satisfies `unchecked_shl`
179+
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
180+
// above), which satisfies `unchecked_shr`
181+
// - because the types are unsigned, the combination are disjoint bits (this is
182+
// not true if they're signed, since SHR will fill in the empty space with a
183+
// sign bit, not zero)
184+
unsafe {
185+
super::disjoint_bitor(
186+
super::unchecked_shl(self, shift),
187+
super::unchecked_shr(rhs, $type::BITS - shift),
188+
)
189+
}
190+
}
191+
}
192+
193+
#[cfg_attr(miri, track_caller)]
194+
#[inline]
195+
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self {
196+
// This implementation is also used by Miri so we have to check the precondition.
197+
// SAFETY: this is guaranteed by the caller
198+
unsafe { super::assume(shift < $type::BITS) };
199+
if shift == 0 {
200+
rhs
201+
} else {
202+
// SAFETY:
203+
// - `shift < T::BITS`, which satisfies `unchecked_shr`
204+
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
205+
// above), which satisfies `unchecked_shl`
206+
// - because the types are unsigned, the combination are disjoint bits (this is
207+
// not true if they're signed, since SHR will fill in the empty space with a
208+
// sign bit, not zero)
209+
unsafe {
210+
super::disjoint_bitor(
211+
super::unchecked_shl(self, $type::BITS - shift),
212+
super::unchecked_shr(rhs, shift),
213+
)
214+
}
215+
}
216+
}
217+
}
218+
)*};
219+
}
220+
221+
impl_funnel_shifts! {
222+
u8, u16, u32, u64, u128, usize
223+
}

library/core/src/intrinsics/mod.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,61 @@ 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+
#[track_caller]
2122+
#[miri::intrinsic_fallback_is_spec]
2123+
pub const unsafe fn unchecked_funnel_shl<T: [const] fallback::FunnelShift>(
2124+
a: T,
2125+
b: T,
2126+
shift: u32,
2127+
) -> T {
2128+
// SAFETY: caller ensures that `shift` is in-range
2129+
unsafe { a.unchecked_funnel_shl(b, shift) }
2130+
}
2131+
2132+
/// Funnel Shift right.
2133+
///
2134+
/// Concatenates `a` and `b` (with `a` in the most significant half),
2135+
/// creating an integer twice as wide. Then shift this integer right
2136+
/// by `shift` (taken modulo the bit size of `T`), and extract the
2137+
/// least significant half. If `a` and `b` are the same, this is equivalent
2138+
/// to a rotate right operation.
2139+
///
2140+
/// It is undefined behavior if `shift` is greater than or equal to the
2141+
/// bit size of `T`.
2142+
///
2143+
/// Safer versions of this intrinsic are available on the integer primitives
2144+
/// via the `funnel_shr` method. For example, [`u32::funnel_shr`]
2145+
#[rustc_intrinsic]
2146+
#[rustc_nounwind]
2147+
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
2148+
#[unstable(feature = "funnel_shifts", issue = "145686")]
2149+
#[track_caller]
2150+
#[miri::intrinsic_fallback_is_spec]
2151+
pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>(
2152+
a: T,
2153+
b: T,
2154+
shift: u32,
2155+
) -> T {
2156+
// SAFETY: caller ensures that `shift` is in-range
2157+
unsafe { a.unchecked_funnel_shr(b, shift) }
2158+
}
2159+
21052160
/// This is an implementation detail of [`crate::ptr::read`] and should
21062161
/// not be used anywhere else. See its comments for why this exists.
21072162
///

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/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#![feature(fmt_internals)]
5151
#![feature(formatting_options)]
5252
#![feature(freeze)]
53+
#![feature(funnel_shifts)]
5354
#![feature(future_join)]
5455
#![feature(generic_assert_internals)]
5556
#![feature(hasher_prefixfree_extras)]

0 commit comments

Comments
 (0)