Skip to content

Commit

Permalink
MIR Shl/Shr: the offset can be computed with rem_euclid
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Jun 14, 2024
1 parent d2ad293 commit 669a400
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 14 deletions.
19 changes: 7 additions & 12 deletions compiler/rustc_const_eval/src/interpret/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,25 +112,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {

// Shift ops can have an RHS with a different numeric type.
if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
let size = left.layout.size.bits();
let l_bits = left.layout.size.bits();
// Compute the equivalent shift modulo `size` that is in the range `0..size`. (This is
// the one MIR operator that does *not* directly map to a single LLVM operation.)
let (shift_amount, overflow) = if right.layout.abi.is_signed() {
let shift_amount = r_signed();
let overflow = shift_amount < 0 || shift_amount >= i128::from(size);
// Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result
// of the `as` will be equal modulo `size` (since it is a power of two).
let masked_amount = (shift_amount as u128) % u128::from(size);
assert_eq!(overflow, shift_amount != i128::try_from(masked_amount).unwrap());
(masked_amount, overflow)
let rem = shift_amount.rem_euclid(l_bits.into());
// `rem` is guaranteed positive, so the `unwrap` cannot fail
(u128::try_from(rem).unwrap(), rem != shift_amount)
} else {
let shift_amount = r_unsigned();
let overflow = shift_amount >= u128::from(size);
let masked_amount = shift_amount % u128::from(size);
assert_eq!(overflow, shift_amount != masked_amount);
(masked_amount, overflow)
let rem = shift_amount.rem_euclid(l_bits.into());
(rem, rem != shift_amount)
};
let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit
let shift_amount = u32::try_from(shift_amount).unwrap(); // we brought this in the range `0..size` so this will always fit
// Compute the shifted result.
let result = if left.layout.abi.is_signed() {
let l = l_signed();
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1490,15 +1490,17 @@ pub enum BinOp {
BitOr,
/// The `<<` operator (shift left)
///
/// The offset is (uniquely) determined as follows:
/// The offset is given by `RHS.rem_euclid(LHS::BITS)`.
/// In other words, it is (uniquely) determined as follows:
/// - it is "equal modulo LHS::BITS" to the RHS
/// - it is in the range `0..LHS::BITS`
Shl,
/// Like `Shl`, but is UB if the RHS >= LHS::BITS or RHS < 0
ShlUnchecked,
/// The `>>` operator (shift right)
///
/// The offset is (uniquely) determined as follows:
/// The offset is given by `RHS.rem_euclid(LHS::BITS)`.
/// In other words, it is (uniquely) determined as follows:
/// - it is "equal modulo LHS::BITS" to the RHS
/// - it is in the range `0..LHS::BITS`
///
Expand Down

0 comments on commit 669a400

Please sign in to comment.