Skip to content

Commit 89fe961

Browse files
committed
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`
2 parents 69d4d5f + a00db66 commit 89fe961

File tree

6 files changed

+47
-44
lines changed

6 files changed

+47
-44
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
378378
| sym::ctpop
379379
| sym::bswap
380380
| sym::bitreverse
381-
| sym::rotate_left
382-
| sym::rotate_right
383381
| sym::saturating_add
384382
| sym::saturating_sub
385383
| sym::unchecked_funnel_shl
@@ -424,19 +422,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
424422
sym::bitreverse => {
425423
self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])
426424
}
427-
sym::rotate_left
428-
| sym::rotate_right
429-
| sym::unchecked_funnel_shl
430-
| sym::unchecked_funnel_shr => {
431-
let is_left = name == sym::rotate_left || name == sym::unchecked_funnel_shl;
425+
sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => {
426+
let is_left = name == sym::unchecked_funnel_shl;
432427
let lhs = args[0].immediate();
433-
let (rhs, raw_shift) =
434-
if name == sym::rotate_left || name == sym::rotate_right {
435-
// rotate = funnel shift with first two args the same
436-
(lhs, args[1].immediate())
437-
} else {
438-
(args[1].immediate(), args[2].immediate())
439-
};
428+
let rhs = args[1].immediate();
429+
let raw_shift = args[2].immediate();
440430
let llvm_name = format!("llvm.fsh{}", if is_left { 'l' } else { 'r' });
441431

442432
// llvm expects shift to be the same type as the values, but rust

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -333,29 +333,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
333333
let r = self.read_immediate(&args[1])?;
334334
self.exact_div(&l, &r, dest)?;
335335
}
336-
sym::rotate_left | sym::rotate_right => {
337-
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
338-
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
339-
let layout_val = self.layout_of(instance_args.type_at(0))?;
340-
let val = self.read_scalar(&args[0])?;
341-
let val_bits = val.to_bits(layout_val.size)?; // sign is ignored here
342-
343-
let layout_raw_shift = self.layout_of(self.tcx.types.u32)?;
344-
let raw_shift = self.read_scalar(&args[1])?;
345-
let raw_shift_bits = raw_shift.to_bits(layout_raw_shift.size)?;
346-
347-
let width_bits = u128::from(layout_val.size.bits());
348-
let shift_bits = raw_shift_bits % width_bits;
349-
let inv_shift_bits = (width_bits - shift_bits) % width_bits;
350-
let result_bits = if intrinsic_name == sym::rotate_left {
351-
(val_bits << shift_bits) | (val_bits >> inv_shift_bits)
352-
} else {
353-
(val_bits >> shift_bits) | (val_bits << inv_shift_bits)
354-
};
355-
let truncated_bits = layout_val.size.truncate(result_bits);
356-
let result = Scalar::from_uint(truncated_bits, layout_val.size);
357-
self.write_scalar(result, dest)?;
358-
}
359336
sym::copy => {
360337
self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
361338
}

library/core/src/intrinsics/mod.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656

5757
use crate::ffi::va_list::{VaArgSafe, VaListImpl};
5858
use crate::marker::{ConstParamTy, Destruct, DiscriminantKind, PointeeSized, Tuple};
59-
use crate::ptr;
59+
use crate::{mem, ptr};
6060

6161
mod bounds;
6262
pub mod fallback;
@@ -2013,7 +2013,14 @@ pub const unsafe fn unchecked_mul<T: Copy>(x: T, y: T) -> T;
20132013
#[rustc_intrinsic_const_stable_indirect]
20142014
#[rustc_nounwind]
20152015
#[rustc_intrinsic]
2016-
pub const fn rotate_left<T: Copy>(x: T, shift: u32) -> T;
2016+
#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)]
2017+
#[miri::intrinsic_fallback_is_spec]
2018+
pub const fn rotate_left<T: [const] fallback::FunnelShift>(x: T, shift: u32) -> T {
2019+
// Make sure to call the intrinsic for `funnel_shl`, not the fallback impl.
2020+
// SAFETY: we modulo `shift` so that the result is definitely less than the size of
2021+
// `T` in bits.
2022+
unsafe { unchecked_funnel_shl(x, x, shift % (mem::size_of::<T>() as u32 * 8)) }
2023+
}
20172024

20182025
/// Performs rotate right.
20192026
///
@@ -2028,7 +2035,14 @@ pub const fn rotate_left<T: Copy>(x: T, shift: u32) -> T;
20282035
#[rustc_intrinsic_const_stable_indirect]
20292036
#[rustc_nounwind]
20302037
#[rustc_intrinsic]
2031-
pub const fn rotate_right<T: Copy>(x: T, shift: u32) -> T;
2038+
#[rustc_allow_const_fn_unstable(const_trait_impl, funnel_shifts)]
2039+
#[miri::intrinsic_fallback_is_spec]
2040+
pub const fn rotate_right<T: [const] fallback::FunnelShift>(x: T, shift: u32) -> T {
2041+
// Make sure to call the intrinsic for `funnel_shr`, not the fallback impl.
2042+
// SAFETY: we modulo `shift` so that the result is definitely less than the size of
2043+
// `T` in bits.
2044+
unsafe { unchecked_funnel_shr(x, x, shift % (mem::size_of::<T>() as u32 * 8)) }
2045+
}
20322046

20332047
/// Returns (a + b) mod 2<sup>N</sup>, where N is the width of T in bits.
20342048
///

library/core/src/num/int_macros.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ macro_rules! int_impl {
275275
/// Shifts the bits to the left by a specified amount, `n`,
276276
/// wrapping the truncated bits to the end of the resulting integer.
277277
///
278+
/// `rotate_left(n)` is equivalent to applying `rotate_left(1)` a total of `n` times. In
279+
/// particular, a rotation by the number of bits in `self` returns the input value
280+
/// unchanged.
281+
///
278282
/// Please note this isn't the same operation as the `<<` shifting operator!
279283
///
280284
/// # Examples
@@ -284,6 +288,7 @@ macro_rules! int_impl {
284288
#[doc = concat!("let m = ", $rot_result, ";")]
285289
///
286290
#[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")]
291+
#[doc = concat!("assert_eq!(n.rotate_left(1024), n);")]
287292
/// ```
288293
#[stable(feature = "rust1", since = "1.0.0")]
289294
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]
@@ -298,6 +303,10 @@ macro_rules! int_impl {
298303
/// wrapping the truncated bits to the beginning of the resulting
299304
/// integer.
300305
///
306+
/// `rotate_right(n)` is equivalent to applying `rotate_right(1)` a total of `n` times. In
307+
/// particular, a rotation by the number of bits in `self` returns the input value
308+
/// unchanged.
309+
///
301310
/// Please note this isn't the same operation as the `>>` shifting operator!
302311
///
303312
/// # Examples
@@ -307,6 +316,7 @@ macro_rules! int_impl {
307316
#[doc = concat!("let m = ", $rot_op, ";")]
308317
///
309318
#[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")]
319+
#[doc = concat!("assert_eq!(n.rotate_right(1024), n);")]
310320
/// ```
311321
#[stable(feature = "rust1", since = "1.0.0")]
312322
#[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")]

library/core/src/num/uint_macros.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ macro_rules! uint_impl {
336336
/// Shifts the bits to the left by a specified amount, `n`,
337337
/// wrapping the truncated bits to the end of the resulting integer.
338338
///
339+
/// `rotate_left(n)` is equivalent to applying `rotate_left(1)` a total of `n` times. In
340+
/// particular, a rotation by the number of bits in `self` returns the input value
341+
/// unchanged.
342+
///
339343
/// Please note this isn't the same operation as the `<<` shifting operator!
340344
///
341345
/// # Examples
@@ -345,12 +349,14 @@ macro_rules! uint_impl {
345349
#[doc = concat!("let m = ", $rot_result, ";")]
346350
///
347351
#[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")]
352+
#[doc = concat!("assert_eq!(n.rotate_left(1024), n);")]
348353
/// ```
349354
#[stable(feature = "rust1", since = "1.0.0")]
350355
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
351356
#[must_use = "this returns the result of the operation, \
352357
without modifying the original"]
353358
#[inline(always)]
359+
#[rustc_allow_const_fn_unstable(const_trait_impl)] // for the intrinsic fallback
354360
pub const fn rotate_left(self, n: u32) -> Self {
355361
return intrinsics::rotate_left(self, n);
356362
}
@@ -359,6 +365,10 @@ macro_rules! uint_impl {
359365
/// wrapping the truncated bits to the beginning of the resulting
360366
/// integer.
361367
///
368+
/// `rotate_right(n)` is equivalent to applying `rotate_right(1)` a total of `n` times. In
369+
/// particular, a rotation by the number of bits in `self` returns the input value
370+
/// unchanged.
371+
///
362372
/// Please note this isn't the same operation as the `>>` shifting operator!
363373
///
364374
/// # Examples
@@ -368,12 +378,14 @@ macro_rules! uint_impl {
368378
#[doc = concat!("let m = ", $rot_op, ";")]
369379
///
370380
#[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")]
381+
#[doc = concat!("assert_eq!(n.rotate_right(1024), n);")]
371382
/// ```
372383
#[stable(feature = "rust1", since = "1.0.0")]
373384
#[rustc_const_stable(feature = "const_math", since = "1.32.0")]
374385
#[must_use = "this returns the result of the operation, \
375386
without modifying the original"]
376387
#[inline(always)]
388+
#[rustc_allow_const_fn_unstable(const_trait_impl)] // for the intrinsic fallback
377389
pub const fn rotate_right(self, n: u32) -> Self {
378390
return intrinsics::rotate_right(self, n);
379391
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ compile-flags: -C no-prepopulate-passes
1+
//@ compile-flags: -O
22

33
#![crate_type = "lib"]
44
#![feature(core_intrinsics)]
@@ -9,7 +9,7 @@ use std::intrinsics::rotate_left;
99
#[no_mangle]
1010
pub unsafe fn rotate_left_u16(x: u16, shift: u32) -> u16 {
1111
// CHECK: %[[tmp:.*]] = trunc i32 %shift to i16
12-
// CHECK: call i16 @llvm.fshl.i16(i16 %x, i16 %x, i16 %[[tmp]])
12+
// CHECK: call noundef i16 @llvm.fshl.i16(i16 %x, i16 %x, i16 %[[tmp]])
1313
rotate_left(x, shift)
1414
}
1515

@@ -18,14 +18,14 @@ pub unsafe fn rotate_left_u16(x: u16, shift: u32) -> u16 {
1818
pub unsafe fn rotate_left_u32(x: u32, shift: u32) -> u32 {
1919
// CHECK-NOT: trunc
2020
// CHECK-NOT: zext
21-
// CHECK: call i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %shift)
21+
// CHECK: call noundef i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 %shift)
2222
rotate_left(x, shift)
2323
}
2424

2525
// CHECK-LABEL: @rotate_left_u64
2626
#[no_mangle]
2727
pub unsafe fn rotate_left_u64(x: u64, shift: u32) -> u64 {
2828
// CHECK: %[[tmp:.*]] = zext i32 %shift to i64
29-
// CHECK: call i64 @llvm.fshl.i64(i64 %x, i64 %x, i64 %[[tmp]])
29+
// CHECK: call noundef i64 @llvm.fshl.i64(i64 %x, i64 %x, i64 %[[tmp]])
3030
rotate_left(x, shift)
3131
}

0 commit comments

Comments
 (0)