diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0626cb3f2f16b..9d5fe20cd40f2 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -377,8 +377,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::ctpop | sym::bswap | sym::bitreverse - | sym::rotate_left - | sym::rotate_right | sym::saturating_add | sym::saturating_sub | sym::unchecked_funnel_shl @@ -423,19 +421,11 @@ 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 - | sym::unchecked_funnel_shl - | sym::unchecked_funnel_shr => { - let is_left = name == sym::rotate_left || name == sym::unchecked_funnel_shl; + sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => { + let is_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 rhs = args[1].immediate(); + let raw_shift = 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 diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 58d7f8ef8c637..ffd7833dba1e1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -333,29 +333,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let r = self.read_immediate(&args[1])?; self.exact_div(&l, &r, dest)?; } - sym::rotate_left | sym::rotate_right => { - // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW)) - // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) - let layout_val = self.layout_of(instance_args.type_at(0))?; - let val = self.read_scalar(&args[0])?; - let val_bits = val.to_bits(layout_val.size)?; // sign is ignored here - - let layout_raw_shift = self.layout_of(self.tcx.types.u32)?; - let raw_shift = self.read_scalar(&args[1])?; - let raw_shift_bits = raw_shift.to_bits(layout_raw_shift.size)?; - - let width_bits = u128::from(layout_val.size.bits()); - let shift_bits = raw_shift_bits % width_bits; - let inv_shift_bits = (width_bits - shift_bits) % width_bits; - let result_bits = if intrinsic_name == sym::rotate_left { - (val_bits << shift_bits) | (val_bits >> inv_shift_bits) - } else { - (val_bits >> shift_bits) | (val_bits << inv_shift_bits) - }; - let truncated_bits = layout_val.size.truncate(result_bits); - let result = Scalar::from_uint(truncated_bits, layout_val.size); - self.write_scalar(result, dest)?; - } sym::copy => { self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?; } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 5ba2d92a4596f..d9467b8a43ace 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -56,7 +56,7 @@ use crate::ffi::va_list::{VaArgSafe, VaListImpl}; use crate::marker::{ConstParamTy, Destruct, DiscriminantKind, PointeeSized, Tuple}; -use crate::ptr; +use crate::{mem, ptr}; mod bounds; pub mod fallback; @@ -2017,7 +2017,14 @@ pub const unsafe fn unchecked_mul(x: T, y: T) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn rotate_left(x: T, shift: u32) -> T; +#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)] +#[miri::intrinsic_fallback_is_spec] +pub const fn rotate_left(x: T, shift: u32) -> T { + // Make sure to call the intrinsic for `funnel_shl`, not the fallback impl. + // SAFETY: we modulo `shift` so that the result is definitely less than the size of + // `T` in bits. + unsafe { unchecked_funnel_shl(x, x, shift % (mem::size_of::() as u32 * 8)) } +} /// Performs rotate right. /// @@ -2032,7 +2039,14 @@ pub const fn rotate_left(x: T, shift: u32) -> T; #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] -pub const fn rotate_right(x: T, shift: u32) -> T; +#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)] +#[miri::intrinsic_fallback_is_spec] +pub const fn rotate_right(x: T, shift: u32) -> T { + // Make sure to call the intrinsic for `funnel_shr`, not the fallback impl. + // SAFETY: we modulo `shift` so that the result is definitely less than the size of + // `T` in bits. + unsafe { unchecked_funnel_shr(x, x, shift % (mem::size_of::() as u32 * 8)) } +} /// Returns (a + b) mod 2N, where N is the width of T in bits. /// diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 16f85c71403ab..d5e60f7cc81b0 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -275,6 +275,10 @@ macro_rules! int_impl { /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// + /// `rotate_left(n)` is equivalent to applying `rotate_left(1)` a total of `n` times. In + /// particular, a rotation by the number of bits in `self` returns the input value + /// unchanged. + /// /// Please note this isn't the same operation as the `<<` shifting operator! /// /// # Examples @@ -284,6 +288,7 @@ macro_rules! int_impl { #[doc = concat!("let m = ", $rot_result, ";")] /// #[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")] + #[doc = concat!("assert_eq!(n.rotate_left(1024), n);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] @@ -298,6 +303,10 @@ macro_rules! int_impl { /// wrapping the truncated bits to the beginning of the resulting /// integer. /// + /// `rotate_right(n)` is equivalent to applying `rotate_right(1)` a total of `n` times. In + /// particular, a rotation by the number of bits in `self` returns the input value + /// unchanged. + /// /// Please note this isn't the same operation as the `>>` shifting operator! /// /// # Examples @@ -307,6 +316,7 @@ macro_rules! int_impl { #[doc = concat!("let m = ", $rot_op, ";")] /// #[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")] + #[doc = concat!("assert_eq!(n.rotate_right(1024), n);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1efc551d670ab..02b94a092b91c 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -336,6 +336,10 @@ macro_rules! uint_impl { /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// + /// `rotate_left(n)` is equivalent to applying `rotate_left(1)` a total of `n` times. In + /// particular, a rotation by the number of bits in `self` returns the input value + /// unchanged. + /// /// Please note this isn't the same operation as the `<<` shifting operator! /// /// # Examples @@ -345,12 +349,14 @@ macro_rules! uint_impl { #[doc = concat!("let m = ", $rot_result, ";")] /// #[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")] + #[doc = concat!("assert_eq!(n.rotate_left(1024), n);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] + #[rustc_allow_const_fn_unstable(const_trait_impl)] // for the intrinsic fallback pub const fn rotate_left(self, n: u32) -> Self { return intrinsics::rotate_left(self, n); } @@ -359,6 +365,10 @@ macro_rules! uint_impl { /// wrapping the truncated bits to the beginning of the resulting /// integer. /// + /// `rotate_right(n)` is equivalent to applying `rotate_right(1)` a total of `n` times. In + /// particular, a rotation by the number of bits in `self` returns the input value + /// unchanged. + /// /// Please note this isn't the same operation as the `>>` shifting operator! /// /// # Examples @@ -368,12 +378,14 @@ macro_rules! uint_impl { #[doc = concat!("let m = ", $rot_op, ";")] /// #[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")] + #[doc = concat!("assert_eq!(n.rotate_right(1024), n);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] + #[rustc_allow_const_fn_unstable(const_trait_impl)] // for the intrinsic fallback pub const fn rotate_right(self, n: u32) -> Self { return intrinsics::rotate_right(self, n); } diff --git a/tests/codegen-llvm/intrinsics/rotate_left.rs b/tests/codegen-llvm/intrinsics/rotate_left.rs index 4f6c5cbaed6aa..fe3c9c5e90d8e 100644 --- a/tests/codegen-llvm/intrinsics/rotate_left.rs +++ b/tests/codegen-llvm/intrinsics/rotate_left.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -C no-prepopulate-passes +//@ compile-flags: -O #![crate_type = "lib"] #![feature(core_intrinsics)] @@ -9,7 +9,7 @@ use std::intrinsics::rotate_left; #[no_mangle] pub unsafe fn rotate_left_u16(x: u16, shift: u32) -> u16 { // CHECK: %[[tmp:.*]] = trunc i32 %shift to i16 - // CHECK: call i16 @llvm.fshl.i16(i16 %x, i16 %x, i16 %[[tmp]]) + // CHECK: call noundef i16 @llvm.fshl.i16(i16 %x, i16 %x, i16 %[[tmp]]) rotate_left(x, shift) } @@ -18,7 +18,7 @@ pub unsafe fn rotate_left_u16(x: u16, shift: u32) -> u16 { pub unsafe fn rotate_left_u32(x: u32, shift: u32) -> u32 { // CHECK-NOT: trunc // CHECK-NOT: zext - // CHECK: call i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %shift) + // CHECK: call noundef i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %shift) rotate_left(x, shift) } @@ -26,6 +26,6 @@ pub unsafe fn rotate_left_u32(x: u32, shift: u32) -> u32 { #[no_mangle] pub unsafe fn rotate_left_u64(x: u64, shift: u32) -> u64 { // CHECK: %[[tmp:.*]] = zext i32 %shift to i64 - // CHECK: call i64 @llvm.fshl.i64(i64 %x, i64 %x, i64 %[[tmp]]) + // CHECK: call noundef i64 @llvm.fshl.i64(i64 %x, i64 %x, i64 %[[tmp]]) rotate_left(x, shift) }