Auto merge of #148478 - RalfJung:rotating-funnel, r=Mark-Simulacrum
use funnel shift as fallback impl for rotating shifts That lets us remove this gnarly implementation from Miri and const-eval. However, `rotate_left`/`rotate_right` are stable as const fn, so to do this we have to `rustc_allow_const_fn_unstable` a bunch of const trait stuff. Is that a bad idea? Cc `@oli-obk` `@fee1-dead`
This commit is contained in:
commit
89fe96197d
6 changed files with 47 additions and 44 deletions
|
|
@ -378,8 +378,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
|
||||
|
|
@ -424,19 +422,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
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -2013,7 +2013,14 @@ pub const unsafe fn unchecked_mul<T: Copy>(x: T, y: T) -> T;
|
|||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn rotate_left<T: Copy>(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<T: [const] fallback::FunnelShift>(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::<T>() as u32 * 8)) }
|
||||
}
|
||||
|
||||
/// Performs rotate right.
|
||||
///
|
||||
|
|
@ -2028,7 +2035,14 @@ pub const fn rotate_left<T: Copy>(x: T, shift: u32) -> T;
|
|||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_nounwind]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn rotate_right<T: Copy>(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<T: [const] fallback::FunnelShift>(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::<T>() as u32 * 8)) }
|
||||
}
|
||||
|
||||
/// Returns (a + b) mod 2<sup>N</sup>, where N is the width of T in bits.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue