From bcfc9b5073a92bbb4b1e4db2eab535357d8973ad Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 21 Aug 2025 14:32:10 +0100 Subject: inline at the callsite & warn when target features mismatch Co-authored-by: Jamie Cunliffe --- compiler/rustc_span/src/symbol.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'compiler/rustc_span/src') diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 585968044bf..bb32737a65c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2154,6 +2154,7 @@ symbols! { target_family, target_feature, target_feature_11, + target_feature_inline_always, target_has_atomic, target_has_atomic_equal_alignment, target_has_atomic_load_store, -- cgit 1.4.1-3-g733a5 From 5b43244ac59119870c9e0f6b642340ab88355b23 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 28 Aug 2025 23:31:46 +0800 Subject: Add LSX accelerated implementation for source file analysis This patch introduces an LSX-optimized version of `analyze_source_file` for the `loongarch64` target. Similar to existing SSE2 implementation for x86, this version: - Processes 16-byte chunks at a time using LSX vector intrinsics. - Quickly identifies newlines in ASCII-only chunks. - Falls back to the generic implementation when multi-byte UTF-8 characters are detected or in the tail portion. --- compiler/rustc_span/src/analyze_source_file.rs | 109 ++++++++++++++++++++++++- compiler/rustc_span/src/lib.rs | 1 + 2 files changed, 107 insertions(+), 3 deletions(-) (limited to 'compiler/rustc_span/src') diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index c32593a6d95..bb2cda77dff 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -81,8 +81,8 @@ cfg_select! { // use `loadu`, which supports unaligned loading. let chunk = unsafe { _mm_loadu_si128(chunk.as_ptr() as *const __m128i) }; - // For character in the chunk, see if its byte value is < 0, which - // indicates that it's part of a UTF-8 char. + // For each character in the chunk, see if its byte value is < 0, + // which indicates that it's part of a UTF-8 char. let multibyte_test = _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)); // Create a bit mask from the comparison results. let multibyte_mask = _mm_movemask_epi8(multibyte_test); @@ -132,8 +132,111 @@ cfg_select! { } } } + target_arch = "loongarch64" => { + fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + use std::arch::is_loongarch_feature_detected; + + if is_loongarch_feature_detected!("lsx") { + unsafe { + analyze_source_file_lsx(src, lines, multi_byte_chars); + } + } else { + analyze_source_file_generic( + src, + src.len(), + RelativeBytePos::from_u32(0), + lines, + multi_byte_chars, + ); + } + } + + /// Checks 16 byte chunks of text at a time. If the chunk contains + /// something other than printable ASCII characters and newlines, the + /// function falls back to the generic implementation. Otherwise it uses + /// LSX intrinsics to quickly find all newlines. + #[target_feature(enable = "lsx")] + unsafe fn analyze_source_file_lsx( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + use std::arch::loongarch64::*; + + const CHUNK_SIZE: usize = 16; + + let (chunks, tail) = src.as_bytes().as_chunks::(); + + // This variable keeps track of where we should start decoding a + // chunk. If a multi-byte character spans across chunk boundaries, + // we need to skip that part in the next chunk because we already + // handled it. + let mut intra_chunk_offset = 0; + + for (chunk_index, chunk) in chunks.iter().enumerate() { + // All LSX memory instructions support unaligned access, so using + // vld is fine. + let chunk = unsafe { lsx_vld::<0>(chunk.as_ptr() as *const i8) }; + + // For each character in the chunk, see if its byte value is < 0, + // which indicates that it's part of a UTF-8 char. + let multibyte_mask = lsx_vmskltz_b(chunk); + // Create a bit mask from the comparison results. + let multibyte_mask = lsx_vpickve2gr_w::<0>(multibyte_mask); + + // If the bit mask is all zero, we only have ASCII chars here: + if multibyte_mask == 0 { + assert!(intra_chunk_offset == 0); + + // Check for newlines in the chunk + let newlines_test = lsx_vseqi_b::<{b'\n' as i32}>(chunk); + let newlines_mask = lsx_vmskltz_b(newlines_test); + let mut newlines_mask = lsx_vpickve2gr_w::<0>(newlines_mask); + + let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); + + while newlines_mask != 0 { + let index = newlines_mask.trailing_zeros(); + + lines.push(RelativeBytePos(index) + output_offset); + + // Clear the bit, so we can find the next one. + newlines_mask &= newlines_mask - 1; + } + } else { + // The slow path. + // There are multibyte chars in here, fallback to generic decoding. + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start..], + CHUNK_SIZE - intra_chunk_offset, + RelativeBytePos::from_usize(scan_start), + lines, + multi_byte_chars, + ); + } + } + + // There might still be a tail left to analyze + let tail_start = src.len() - tail.len() + intra_chunk_offset; + if tail_start < src.len() { + analyze_source_file_generic( + &src[tail_start..], + src.len() - tail_start, + RelativeBytePos::from_usize(tail_start), + lines, + multi_byte_chars, + ); + } + } + } _ => { - // The target (or compiler version) does not support SSE2 ... + // The target (or compiler version) does not support vector instructions + // our specialized implementations need (x86 SSE2, loongarch64 LSX)... fn analyze_source_file_dispatch( src: &str, lines: &mut Vec, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index ae6755f0764..8907c5e4c4a 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -18,6 +18,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![cfg_attr(bootstrap, feature(round_char_boundary))] +#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] -- cgit 1.4.1-3-g733a5 From 62b4347e80cc86314bd98749e95eff8cdf8ef005 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 21 Aug 2025 04:47:05 +0530 Subject: Add `funnel_sh{l,r}` functions and intrinsics - Add a fallback implementation for the intrinsics - Add LLVM backend support for funnel shifts Co-Authored-By: folkertdev --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 26 +++++--- compiler/rustc_hir_analysis/src/check/intrinsic.rs | 3 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/intrinsics/fallback.rs | 73 ++++++++++++++++++++++ library/core/src/intrinsics/mod.rs | 55 ++++++++++++++++ library/core/src/lib.rs | 1 + library/core/src/num/mod.rs | 24 +++++++ library/core/src/num/uint_macros.rs | 73 ++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + library/coretests/tests/num/uint_macros.rs | 36 +++++++++++ library/std/src/lib.rs | 1 + src/tools/miri/tests/fail/intrinsics/funnel_shl.rs | 7 +++ .../miri/tests/fail/intrinsics/funnel_shl.stderr | 15 +++++ src/tools/miri/tests/fail/intrinsics/funnel_shr.rs | 7 +++ .../miri/tests/fail/intrinsics/funnel_shr.stderr | 15 +++++ src/tools/miri/tests/pass/intrinsics/integer.rs | 8 ++- 16 files changed, 338 insertions(+), 9 deletions(-) create mode 100644 src/tools/miri/tests/fail/intrinsics/funnel_shl.rs create mode 100644 src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr create mode 100644 src/tools/miri/tests/fail/intrinsics/funnel_shr.rs create mode 100644 src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr (limited to 'compiler/rustc_span/src') diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 49d3dedbeab..85f71f331a4 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -383,7 +383,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::rotate_left | sym::rotate_right | sym::saturating_add - | sym::saturating_sub => { + | sym::saturating_sub + | sym::unchecked_funnel_shl + | sym::unchecked_funnel_shr => { let ty = args[0].layout.ty; if !ty.is_integral() { tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { @@ -424,18 +426,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::bitreverse => { self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()]) } - sym::rotate_left | sym::rotate_right => { - let is_left = name == sym::rotate_left; - let val = args[0].immediate(); - let raw_shift = args[1].immediate(); - // rotate = funnel shift with first two args the same + sym::rotate_left + | sym::rotate_right + | sym::unchecked_funnel_shl + | sym::unchecked_funnel_shr => { + let is_left = name == sym::rotate_left || name == sym::unchecked_funnel_shl; + let lhs = args[0].immediate(); + let (rhs, raw_shift) = + if name == sym::rotate_left || name == sym::rotate_right { + // rotate = funnel shift with first two args the same + (lhs, args[1].immediate()) + } else { + (args[1].immediate(), args[2].immediate()) + }; let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' }); // llvm expects shift to be the same type as the values, but rust // always uses `u32`. - let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); + let raw_shift = self.intcast(raw_shift, self.val_ty(lhs), false); - self.call_intrinsic(llvm_name, &[llty], &[val, val, raw_shift]) + self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs, raw_shift]) } sym::saturating_add | sym::saturating_sub => { let is_add = name == sym::saturating_add; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cfc6bc2f3a0..aa2d27ab809 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -449,6 +449,9 @@ pub(crate) fn check_intrinsic_type( } sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)), sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), tcx.types.u32], param(0)), + sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => { + (1, 0, vec![param(0), param(0), tcx.types.u32], param(0)) + } sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { (1, 0, vec![param(0), param(0)], param(0)) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index dcb1becc957..64662d48137 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2269,6 +2269,8 @@ symbols! { unboxed_closures, unchecked_add, unchecked_div, + unchecked_funnel_shl, + unchecked_funnel_shr, unchecked_mul, unchecked_rem, unchecked_shl, diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs index eec5c4d646d..96928325328 100644 --- a/library/core/src/intrinsics/fallback.rs +++ b/library/core/src/intrinsics/fallback.rs @@ -148,3 +148,76 @@ impl_disjoint_bitor! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, } + +#[const_trait] +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +pub trait FunnelShift: Copy + 'static { + /// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle + /// different types since calling intrinsics with generics doesn't work. + unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self; + + /// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle + /// different types since calling intrinsics with generics doesn't work. + unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self; +} + +macro_rules! impl_funnel_shifts { + ($($type:ident),*) => {$( + #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] + impl const FunnelShift for $type { + #[cfg_attr(miri, track_caller)] + #[inline] + unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self { + // This implementation is also used by Miri so we have to check the precondition. + // SAFETY: this is guaranteed by the caller + unsafe { super::assume(shift < $type::BITS) }; + if shift == 0 { + self + } else { + // SAFETY: + // - `shift < T::BITS`, which satisfies `unchecked_shl` + // - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked + // above), which satisfies `unchecked_shr` + // - because the types are unsigned, the combination are disjoint bits (this is + // not true if they're signed, since SHR will fill in the empty space with a + // sign bit, not zero) + unsafe { + super::disjoint_bitor( + super::unchecked_shl(self, shift), + super::unchecked_shr(rhs, $type::BITS - shift), + ) + } + } + } + + #[cfg_attr(miri, track_caller)] + #[inline] + unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self { + // This implementation is also used by Miri so we have to check the precondition. + // SAFETY: this is guaranteed by the caller + unsafe { super::assume(shift < $type::BITS) }; + if shift == 0 { + rhs + } else { + // SAFETY: + // - `shift < T::BITS`, which satisfies `unchecked_shr` + // - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked + // above), which satisfies `unchecked_shl` + // - because the types are unsigned, the combination are disjoint bits (this is + // not true if they're signed, since SHR will fill in the empty space with a + // sign bit, not zero) + unsafe { + super::disjoint_bitor( + super::unchecked_shl(self, $type::BITS - shift), + super::unchecked_shr(rhs, shift), + ) + } + } + } + } + )*}; +} + +impl_funnel_shifts! { + u8, u16, u32, u64, u128, usize +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 904aa52c784..bffffbc29c1 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2102,6 +2102,61 @@ pub const fn saturating_add(a: T, b: T) -> T; #[rustc_intrinsic] pub const fn saturating_sub(a: T, b: T) -> T; +/// Funnel Shift left. +/// +/// Concatenates `a` and `b` (with `a` in the most significant half), +/// creating an integer twice as wide. Then shift this integer left +/// by `shift`), and extract the most significant half. If `a` and `b` +/// are the same, this is equivalent to a rotate left operation. +/// +/// It is undefined behavior if `shift` is greater than or equal to the +/// bit size of `T`. +/// +/// Safe versions of this intrinsic are available on the integer primitives +/// via the `funnel_shl` method. For example, [`u32::funnel_shl`]. +#[rustc_intrinsic] +#[rustc_nounwind] +#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] +#[unstable(feature = "funnel_shifts", issue = "145686")] +#[track_caller] +#[miri::intrinsic_fallback_is_spec] +pub const unsafe fn unchecked_funnel_shl( + a: T, + b: T, + shift: u32, +) -> T { + // SAFETY: caller ensures that `shift` is in-range + unsafe { a.unchecked_funnel_shl(b, shift) } +} + +/// Funnel Shift right. +/// +/// Concatenates `a` and `b` (with `a` in the most significant half), +/// creating an integer twice as wide. Then shift this integer right +/// by `shift` (taken modulo the bit size of `T`), and extract the +/// least significant half. If `a` and `b` are the same, this is equivalent +/// to a rotate right operation. +/// +/// It is undefined behavior if `shift` is greater than or equal to the +/// bit size of `T`. +/// +/// Safer versions of this intrinsic are available on the integer primitives +/// via the `funnel_shr` method. For example, [`u32::funnel_shr`] +#[rustc_intrinsic] +#[rustc_nounwind] +#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] +#[unstable(feature = "funnel_shifts", issue = "145686")] +#[track_caller] +#[miri::intrinsic_fallback_is_spec] +pub const unsafe fn unchecked_funnel_shr( + a: T, + b: T, + shift: u32, +) -> T { + // SAFETY: caller ensures that `shift` is in-range + unsafe { a.unchecked_funnel_shr(b, shift) } +} + /// This is an implementation detail of [`crate::ptr::read`] and should /// not be used anywhere else. See its comments for why this exists. /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 71abd707374..fb6cacb0e00 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -156,6 +156,7 @@ #![feature(f128)] #![feature(freeze_impls)] #![feature(fundamental)] +#![feature(funnel_shifts)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index acfe38b7a37..e2987b0b7e5 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -454,6 +454,9 @@ impl u8 { rot = 2, rot_op = "0x82", rot_result = "0xa", + fsh_op = "0x36", + fshl_result = "0x8", + fshr_result = "0x8d", swap_op = "0x12", swapped = "0x12", reversed = "0x48", @@ -1088,6 +1091,9 @@ impl u16 { rot = 4, rot_op = "0xa003", rot_result = "0x3a", + fsh_op = "0x2de", + fshl_result = "0x30", + fshr_result = "0x302d", swap_op = "0x1234", swapped = "0x3412", reversed = "0x2c48", @@ -1135,6 +1141,9 @@ impl u32 { rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", + fsh_op = "0x2fe78e45", + fshl_result = "0xb32f", + fshr_result = "0xb32fe78e", swap_op = "0x12345678", swapped = "0x78563412", reversed = "0x1e6a2c48", @@ -1158,6 +1167,9 @@ impl u64 { rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", + fsh_op = "0x2fe78e45983acd98", + fshl_result = "0x6e12fe", + fshr_result = "0x6e12fe78e45983ac", swap_op = "0x1234567890123456", swapped = "0x5634129078563412", reversed = "0x6a2c48091e6a2c48", @@ -1181,6 +1193,9 @@ impl u128 { rot = 16, rot_op = "0x13f40000000000000000000000004f76", rot_result = "0x4f7613f4", + fsh_op = "0x2fe78e45983acd98039000008736273", + fshl_result = "0x4f7602fe", + fshr_result = "0x4f7602fe78e45983acd9803900000873", swap_op = "0x12345678901234567890123456789012", swapped = "0x12907856341290785634129078563412", reversed = "0x48091e6a2c48091e6a2c48091e6a2c48", @@ -1207,6 +1222,9 @@ impl usize { rot = 4, rot_op = "0xa003", rot_result = "0x3a", + fsh_op = "0x2fe78e45983acd98039000008736273", + fshl_result = "0x4f7602fe", + fshr_result = "0x4f7602fe78e45983acd9803900000873", swap_op = "0x1234", swapped = "0x3412", reversed = "0x2c48", @@ -1231,6 +1249,9 @@ impl usize { rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", + fsh_op = "0x2fe78e45", + fshl_result = "0xb32f", + fshr_result = "0xb32fe78e", swap_op = "0x12345678", swapped = "0x78563412", reversed = "0x1e6a2c48", @@ -1255,6 +1276,9 @@ impl usize { rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", + fsh_op = "0x2fe78e45983acd98", + fshl_result = "0x6e12fe", + fshr_result = "0x6e12fe78e45983ac", swap_op = "0x1234567890123456", swapped = "0x5634129078563412", reversed = "0x6a2c48091e6a2c48", diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 10d9498d15e..1fef3c822b7 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -14,6 +14,9 @@ macro_rules! uint_impl { rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, + fsh_op = $fsh_op:literal, + fshl_result = $fshl_result:literal, + fshr_result = $fshr_result:literal, swap_op = $swap_op:literal, swapped = $swapped:literal, reversed = $reversed:literal, @@ -375,6 +378,76 @@ macro_rules! uint_impl { return intrinsics::rotate_right(self, n); } + /// Performs a left funnel shift (concatenates `self` with `rhs`, with `self` + /// making up the most significant half, then shifts the combined value left + /// by `n`, and most significant half is extracted to produce the result). + /// + /// Please note this isn't the same operation as the `<<` shifting operator or + /// [`rotate_left`](Self::rotate_left), although `a.funnel_shl(a, n)` is *equivalent* + /// to `a.rotate_left(n)`. + /// + /// # Panics + /// + /// If `n` is greater than or equal to the number of bits in `self` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(funnel_shifts)] + #[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $fshl_result, ";")] + /// + #[doc = concat!("assert_eq!(a.funnel_shl(b, ", $rot, "), m);")] + /// ``` + #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] + #[unstable(feature = "funnel_shifts", issue = "145686")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn funnel_shl(self, rhs: Self, n: u32) -> Self { + assert!(n < Self::BITS, "attempt to funnel shift left with overflow"); + // SAFETY: just checked that `shift` is in-range + unsafe { intrinsics::unchecked_funnel_shl(self, rhs, n) } + } + + /// Performs a right funnel shift (concatenates `self` and `rhs`, with `self` + /// making up the most significant half, then shifts the combined value right + /// by `n`, and least significant half is extracted to produce the result). + /// + /// Please note this isn't the same operation as the `>>` shifting operator or + /// [`rotate_right`](Self::rotate_right), although `a.funnel_shr(a, n)` is *equivalent* + /// to `a.rotate_right(n)`. + /// + /// # Panics + /// + /// If `n` is greater than or equal to the number of bits in `self` + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(funnel_shifts)] + #[doc = concat!("let a = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let b = ", $fsh_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $fshr_result, ";")] + /// + #[doc = concat!("assert_eq!(a.funnel_shr(b, ", $rot, "), m);")] + /// ``` + #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] + #[unstable(feature = "funnel_shifts", issue = "145686")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn funnel_shr(self, rhs: Self, n: u32) -> Self { + assert!(n < Self::BITS, "attempt to funnel shift right with overflow"); + // SAFETY: just checked that `shift` is in-range + unsafe { intrinsics::unchecked_funnel_shr(self, rhs, n) } + } + /// Reverses the byte order of the integer. /// /// # Examples diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index d2281b1df2f..c16c344776d 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -50,6 +50,7 @@ #![feature(fmt_internals)] #![feature(formatting_options)] #![feature(freeze)] +#![feature(funnel_shifts)] #![feature(future_join)] #![feature(generic_assert_internals)] #![feature(hasher_prefixfree_extras)] diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index c7d10ea4d88..63be8a45b5c 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -104,6 +104,19 @@ macro_rules! uint_module { assert_eq_const_safe!($T: C.rotate_left(128), C); } + fn test_funnel_shift() { + // Shifting by 0 should have no effect + assert_eq_const_safe!($T: <$T>::funnel_shl(A, B, 0), A); + assert_eq_const_safe!($T: <$T>::funnel_shr(A, B, 0), B); + + assert_eq_const_safe!($T: <$T>::funnel_shl(_0, _1, 4), 0b1111); + assert_eq_const_safe!($T: <$T>::funnel_shr(_0, _1, 4), _1 >> 4); + assert_eq_const_safe!($T: <$T>::funnel_shl(_1, _0, 4), _1 << 4); + + assert_eq_const_safe!($T: <$T>::funnel_shl(_1, _1, 4), <$T>::rotate_left(_1, 4)); + assert_eq_const_safe!($T: <$T>::funnel_shr(_1, _1, 4), <$T>::rotate_right(_1, 4)); + } + fn test_swap_bytes() { assert_eq_const_safe!($T: A.swap_bytes().swap_bytes(), A); assert_eq_const_safe!($T: B.swap_bytes().swap_bytes(), B); @@ -150,6 +163,29 @@ macro_rules! uint_module { } } + #[test] + #[should_panic = "attempt to funnel shift left with overflow"] + fn test_funnel_shl_overflow() { + let _ = <$T>::funnel_shl(A, B, $T::BITS); + } + + #[test] + #[should_panic = "attempt to funnel shift right with overflow"] + fn test_funnel_shr_overflow() { + let _ = <$T>::funnel_shr(A, B, $T::BITS); + } + + #[test] + fn test_funnel_shifts_runtime() { + for i in 0..$T::BITS - 1 { + assert_eq!(<$T>::funnel_shl(A, 0, i), A << i); + assert_eq!(<$T>::funnel_shl(A, A, i), A.rotate_left(i)); + + assert_eq!(<$T>::funnel_shr(0, A, i), A >> i); + assert_eq!(<$T>::funnel_shr(A, A, i), A.rotate_right(i)); + } + } + #[test] fn test_isolate_highest_one() { const BITS: $T = <$T>::MAX; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ab417b6c72f..75018424c4e 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -296,6 +296,7 @@ #![feature(f128)] #![feature(ffi_const)] #![feature(formatting_options)] +#![feature(funnel_shifts)] #![feature(hash_map_internals)] #![feature(hash_map_macro)] #![feature(if_let_guard)] diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs new file mode 100644 index 00000000000..40b61f65ebe --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs @@ -0,0 +1,7 @@ +#![feature(core_intrinsics, funnel_shifts)] + +fn main() { + unsafe { + std::intrinsics::unchecked_funnel_shl(1_u32, 2, 32); //~ ERROR: Undefined Behavior + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr b/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr new file mode 100644 index 00000000000..fc828021b13 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `assume` called with `false` + --> tests/fail/intrinsics/funnel_shl.rs:LL:CC + | +LL | std::intrinsics::unchecked_funnel_shl(1_u32, 2, 32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/funnel_shl.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs new file mode 100644 index 00000000000..95822656397 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs @@ -0,0 +1,7 @@ +#![feature(core_intrinsics, funnel_shifts)] + +fn main() { + unsafe { + std::intrinsics::unchecked_funnel_shr(1_u32, 2, 32); //~ ERROR: Undefined Behavior + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr b/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr new file mode 100644 index 00000000000..7361df1b4c7 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `assume` called with `false` + --> tests/fail/intrinsics/funnel_shr.rs:LL:CC + | +LL | std::intrinsics::unchecked_funnel_shr(1_u32, 2, 32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/intrinsics/funnel_shr.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/intrinsics/integer.rs b/src/tools/miri/tests/pass/intrinsics/integer.rs index 13e7bd8e1b9..8727b6d3c87 100644 --- a/src/tools/miri/tests/pass/intrinsics/integer.rs +++ b/src/tools/miri/tests/pass/intrinsics/integer.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) -#![feature(core_intrinsics)] +#![feature(core_intrinsics, funnel_shifts)] use std::intrinsics::*; pub fn main() { @@ -143,5 +143,11 @@ pub fn main() { assert_eq!(unchecked_mul(6u8, 7), 42); assert_eq!(unchecked_mul(13, -5), -65); + + assert_eq!(unchecked_funnel_shl(1_u32, 2, 5), 32); + assert_eq!(unchecked_funnel_shl(1_u32, 2, 31), 0x80000001); + + assert_eq!(unchecked_funnel_shr(1_u32, 2, 5), 0x08000000); + assert_eq!(unchecked_funnel_shr(1_u32, 2, 31), 2); } } -- cgit 1.4.1-3-g733a5