Auto merge of #112724 - scottmcm:simpler-unchecked-shifts, r=Mark-Simulacrum

[libs] Simplify `unchecked_{shl,shr}`

There's no need for the `const_eval_select` dance here.  And while I originally wrote the `.try_into().unwrap_unchecked()` implementation here, it's kinda a mess in MIR -- this new one is substantially simpler, as shown by the old one being above the inlining threshold but the new one being below it in the `mir-opt/inline/unchecked_shifts` tests.

We don't need `u32::checked_shl` doing a dance through both `Result` *and* `Option` 🙃
This commit is contained in:
bors 2023-06-19 04:48:35 +00:00
commit 8d1fa473dd
20 changed files with 449 additions and 99 deletions

View File

@ -3,7 +3,6 @@
#![stable(feature = "rust1", since = "1.0.0")]
use crate::ascii;
use crate::convert::TryInto;
use crate::intrinsics;
use crate::mem;
use crate::ops::{Add, Mul, Sub};
@ -278,18 +277,12 @@ pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
macro_rules! conv_rhs_for_unchecked_shift {
($SelfT:ty, $x:expr) => {{
#[inline]
fn conv(x: u32) -> $SelfT {
// FIXME(const-hack) replace with `.try_into().ok().unwrap_unchecked()`.
// SAFETY: Any legal shift amount must be losslessly representable in the self type.
unsafe { x.try_into().ok().unwrap_unchecked() }
// If the `as` cast will truncate, ensure we still tell the backend
// that the pre-truncation value was also small.
if <$SelfT>::BITS < 32 {
intrinsics::assume($x <= (<$SelfT>::MAX as u32));
}
#[inline]
const fn const_conv(x: u32) -> $SelfT {
x as _
}
intrinsics::const_eval_select(($x,), const_conv, conv)
$x as $SelfT
}};
}

View File

@ -8,6 +8,7 @@
// CHECK-LABEL: @unchecked_shl_unsigned_same
#[no_mangle]
pub unsafe fn unchecked_shl_unsigned_same(a: u32, b: u32) -> u32 {
// CHECK-NOT: assume
// CHECK-NOT: and i32
// CHECK: shl i32 %a, %b
// CHECK-NOT: and i32
@ -30,6 +31,7 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
// CHECK-LABEL: @unchecked_shl_unsigned_bigger
#[no_mangle]
pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
// CHECK-NOT: assume
// CHECK: %[[EXT:.+]] = zext i32 %b to i64
// CHECK: shl i64 %a, %[[EXT]]
a.unchecked_shl(b)
@ -38,6 +40,7 @@ pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
// CHECK-LABEL: @unchecked_shr_signed_same
#[no_mangle]
pub unsafe fn unchecked_shr_signed_same(a: i32, b: u32) -> i32 {
// CHECK-NOT: assume
// CHECK-NOT: and i32
// CHECK: ashr i32 %a, %b
// CHECK-NOT: and i32
@ -60,6 +63,7 @@ pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 {
// CHECK-LABEL: @unchecked_shr_signed_bigger
#[no_mangle]
pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 {
// CHECK-NOT: assume
// CHECK: %[[EXT:.+]] = zext i32 %b to i64
// CHECK: ashr i64 %a, %[[EXT]]
a.unchecked_shr(b)

View File

@ -16,3 +16,15 @@ pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 {
a.unchecked_shr(b)
}
// EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_bigger.Inline.diff
// EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_bigger.PreCodegen.after.mir
pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
a.unchecked_shl(b)
}
// EMIT_MIR unchecked_shifts.unchecked_shr_signed_bigger.Inline.diff
// EMIT_MIR unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.mir
pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 {
a.unchecked_shr(b)
}

View File

@ -0,0 +1,36 @@
- // MIR for `unchecked_shl_unsigned_bigger` before Inline
+ // MIR for `unchecked_shl_unsigned_bigger` after Inline
fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
debug a => _1;
debug b => _2;
let mut _0: u64;
let mut _3: u64;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: u64;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
- _0 = core::num::<impl u64>::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_5);
+ _5 = _4 as u64 (IntToInt);
+ _0 = unchecked_shl::<u64>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;
}
}

View File

@ -0,0 +1,36 @@
- // MIR for `unchecked_shl_unsigned_bigger` before Inline
+ // MIR for `unchecked_shl_unsigned_bigger` after Inline
fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
debug a => _1;
debug b => _2;
let mut _0: u64;
let mut _3: u64;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: u64;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
- _0 = core::num::<impl u64>::unchecked_shl(move _3, move _4) -> bb1;
+ StorageLive(_5);
+ _5 = _4 as u64 (IntToInt);
+ _0 = unchecked_shl::<u64>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;
}
}

View File

@ -0,0 +1,25 @@
// MIR for `unchecked_shl_unsigned_bigger` after PreCodegen
fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
debug a => _1;
debug b => _2;
let mut _0: u64;
scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
debug self => _1;
debug rhs => _2;
let mut _3: u64;
scope 2 {
}
}
bb0: {
StorageLive(_3);
_3 = _2 as u64 (IntToInt);
_0 = unchecked_shl::<u64>(_1, move _3) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_3);
return;
}
}

View File

@ -0,0 +1,25 @@
// MIR for `unchecked_shl_unsigned_bigger` after PreCodegen
fn unchecked_shl_unsigned_bigger(_1: u64, _2: u32) -> u64 {
debug a => _1;
debug b => _2;
let mut _0: u64;
scope 1 (inlined core::num::<impl u64>::unchecked_shl) {
debug self => _1;
debug rhs => _2;
let mut _3: u64;
scope 2 {
}
}
bb0: {
StorageLive(_3);
_3 = _2 as u64 (IntToInt);
_0 = unchecked_shl::<u64>(_1, move _3) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_3);
return;
}
}

View File

@ -7,16 +7,36 @@
let mut _0: u16;
let mut _3: u16;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: u16;
+ let mut _6: bool;
+ let mut _7: u32;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
_0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable];
- _0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_5);
+ StorageLive(_6);
+ StorageLive(_7);
+ _7 = const 65535_u32;
+ _6 = Le(_4, move _7);
+ StorageDead(_7);
+ assume(move _6);
+ StorageDead(_6);
+ _5 = _4 as u16 (IntToInt);
+ _0 = unchecked_shl::<u16>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;

View File

@ -7,16 +7,36 @@
let mut _0: u16;
let mut _3: u16;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: u16;
+ let mut _6: bool;
+ let mut _7: u32;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
_0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> bb1;
- _0 = core::num::<impl u16>::unchecked_shl(move _3, move _4) -> bb1;
+ StorageLive(_5);
+ StorageLive(_6);
+ StorageLive(_7);
+ _7 = const 65535_u32;
+ _6 = Le(_4, move _7);
+ StorageDead(_7);
+ assume(move _6);
+ StorageDead(_6);
+ _5 = _4 as u16 (IntToInt);
+ _0 = unchecked_shl::<u16>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;

View File

@ -4,12 +4,31 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
debug a => _1;
debug b => _2;
let mut _0: u16;
scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
debug self => _1;
debug rhs => _2;
let mut _3: u32;
let mut _4: bool;
let mut _5: u16;
scope 2 {
}
}
bb0: {
_0 = core::num::<impl u16>::unchecked_shl(_1, _2) -> [return: bb1, unwind unreachable];
StorageLive(_5);
StorageLive(_4);
StorageLive(_3);
_3 = const 65535_u32;
_4 = Le(_2, move _3);
StorageDead(_3);
assume(move _4);
StorageDead(_4);
_5 = _2 as u16 (IntToInt);
_0 = unchecked_shl::<u16>(_1, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_5);
return;
}
}

View File

@ -4,12 +4,31 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 {
debug a => _1;
debug b => _2;
let mut _0: u16;
scope 1 (inlined core::num::<impl u16>::unchecked_shl) {
debug self => _1;
debug rhs => _2;
let mut _3: u32;
let mut _4: bool;
let mut _5: u16;
scope 2 {
}
}
bb0: {
_0 = core::num::<impl u16>::unchecked_shl(_1, _2) -> bb1;
StorageLive(_5);
StorageLive(_4);
StorageLive(_3);
_3 = const 65535_u32;
_4 = Le(_2, move _3);
StorageDead(_3);
assume(move _4);
StorageDead(_4);
_5 = _2 as u16 (IntToInt);
_0 = unchecked_shl::<u16>(_1, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_5);
return;
}
}

View File

@ -0,0 +1,36 @@
- // MIR for `unchecked_shr_signed_bigger` before Inline
+ // MIR for `unchecked_shr_signed_bigger` after Inline
fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
debug a => _1;
debug b => _2;
let mut _0: i64;
let mut _3: i64;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: i64;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
- _0 = core::num::<impl i64>::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_5);
+ _5 = _4 as i64 (IntToInt);
+ _0 = unchecked_shr::<i64>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;
}
}

View File

@ -0,0 +1,36 @@
- // MIR for `unchecked_shr_signed_bigger` before Inline
+ // MIR for `unchecked_shr_signed_bigger` after Inline
fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
debug a => _1;
debug b => _2;
let mut _0: i64;
let mut _3: i64;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: i64;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
- _0 = core::num::<impl i64>::unchecked_shr(move _3, move _4) -> bb1;
+ StorageLive(_5);
+ _5 = _4 as i64 (IntToInt);
+ _0 = unchecked_shr::<i64>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;
}
}

View File

@ -0,0 +1,25 @@
// MIR for `unchecked_shr_signed_bigger` after PreCodegen
fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
debug a => _1;
debug b => _2;
let mut _0: i64;
scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
debug self => _1;
debug rhs => _2;
let mut _3: i64;
scope 2 {
}
}
bb0: {
StorageLive(_3);
_3 = _2 as i64 (IntToInt);
_0 = unchecked_shr::<i64>(_1, move _3) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_3);
return;
}
}

View File

@ -0,0 +1,25 @@
// MIR for `unchecked_shr_signed_bigger` after PreCodegen
fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 {
debug a => _1;
debug b => _2;
let mut _0: i64;
scope 1 (inlined core::num::<impl i64>::unchecked_shr) {
debug self => _1;
debug rhs => _2;
let mut _3: i64;
scope 2 {
}
}
bb0: {
StorageLive(_3);
_3 = _2 as i64 (IntToInt);
_0 = unchecked_shr::<i64>(_1, move _3) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_3);
return;
}
}

View File

@ -7,16 +7,36 @@
let mut _0: i16;
let mut _3: i16;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: i16;
+ let mut _6: bool;
+ let mut _7: u32;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
_0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable];
- _0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_5);
+ StorageLive(_6);
+ StorageLive(_7);
+ _7 = const 32767_u32;
+ _6 = Le(_4, move _7);
+ StorageDead(_7);
+ assume(move _6);
+ StorageDead(_6);
+ _5 = _4 as i16 (IntToInt);
+ _0 = unchecked_shr::<i16>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;

View File

@ -7,16 +7,36 @@
let mut _0: i16;
let mut _3: i16;
let mut _4: u32;
+ scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
+ debug self => _3;
+ debug rhs => _4;
+ let mut _5: i16;
+ let mut _6: bool;
+ let mut _7: u32;
+ scope 2 {
+ }
+ }
bb0: {
StorageLive(_3);
_3 = _1;
StorageLive(_4);
_4 = _2;
_0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> bb1;
- _0 = core::num::<impl i16>::unchecked_shr(move _3, move _4) -> bb1;
+ StorageLive(_5);
+ StorageLive(_6);
+ StorageLive(_7);
+ _7 = const 32767_u32;
+ _6 = Le(_4, move _7);
+ StorageDead(_7);
+ assume(move _6);
+ StorageDead(_6);
+ _5 = _4 as i16 (IntToInt);
+ _0 = unchecked_shr::<i16>(_3, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
+ StorageDead(_5);
StorageDead(_4);
StorageDead(_3);
return;

View File

@ -4,12 +4,31 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
debug a => _1;
debug b => _2;
let mut _0: i16;
scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
debug self => _1;
debug rhs => _2;
let mut _3: u32;
let mut _4: bool;
let mut _5: i16;
scope 2 {
}
}
bb0: {
_0 = core::num::<impl i16>::unchecked_shr(_1, _2) -> [return: bb1, unwind unreachable];
StorageLive(_5);
StorageLive(_4);
StorageLive(_3);
_3 = const 32767_u32;
_4 = Le(_2, move _3);
StorageDead(_3);
assume(move _4);
StorageDead(_4);
_5 = _2 as i16 (IntToInt);
_0 = unchecked_shr::<i16>(_1, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_5);
return;
}
}

View File

@ -4,12 +4,31 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 {
debug a => _1;
debug b => _2;
let mut _0: i16;
scope 1 (inlined core::num::<impl i16>::unchecked_shr) {
debug self => _1;
debug rhs => _2;
let mut _3: u32;
let mut _4: bool;
let mut _5: i16;
scope 2 {
}
}
bb0: {
_0 = core::num::<impl i16>::unchecked_shr(_1, _2) -> bb1;
StorageLive(_5);
StorageLive(_4);
StorageLive(_3);
_3 = const 32767_u32;
_4 = Le(_2, move _3);
StorageDead(_3);
assume(move _4);
StorageDead(_4);
_5 = _2 as i16 (IntToInt);
_0 = unchecked_shr::<i16>(_1, move _5) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_5);
return;
}
}

View File

@ -7,17 +7,17 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
scope 1 (inlined core::num::<impl u32>::checked_shl) {
debug self => _1;
debug rhs => _2;
let mut _11: u32;
let mut _12: bool;
let mut _7: u32;
let mut _8: bool;
scope 2 {
debug a => _11;
debug b => _10;
debug a => _7;
debug b => _6;
}
scope 3 (inlined core::num::<impl u32>::overflowing_shl) {
debug self => _1;
debug rhs => _2;
let mut _9: u32;
let mut _10: bool;
let mut _5: u32;
let mut _6: bool;
scope 4 (inlined core::num::<impl u32>::wrapping_shl) {
debug self => _1;
debug rhs => _2;
@ -27,52 +27,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
scope 6 (inlined core::num::<impl u32>::unchecked_shl) {
debug self => _1;
debug rhs => _4;
let mut _8: u32;
scope 7 {
scope 8 (inlined core::num::<impl u32>::unchecked_shl::conv) {
debug x => _4;
let mut _5: std::result::Result<u32, std::convert::Infallible>;
let mut _7: std::option::Option<u32>;
scope 9 {
scope 10 (inlined <u32 as TryInto<u32>>::try_into) {
debug self => _4;
scope 11 (inlined <u32 as TryFrom<u32>>::try_from) {
debug value => _4;
scope 12 (inlined <u32 as Into<u32>>::into) {
debug self => _4;
scope 13 (inlined <u32 as From<u32>>::from) {
debug t => _4;
}
}
}
}
scope 14 (inlined Result::<u32, Infallible>::ok) {
debug self => _5;
let _6: u32;
scope 15 {
debug x => _6;
}
}
scope 16 (inlined #[track_caller] Option::<u32>::unwrap_unchecked) {
debug self => _7;
let mut _13: &std::option::Option<u32>;
scope 17 {
debug val => _8;
}
scope 18 {
scope 20 (inlined unreachable_unchecked) {
scope 21 {
scope 22 (inlined unreachable_unchecked::runtime) {
}
}
}
}
scope 19 (inlined Option::<u32>::is_some) {
debug self => _13;
}
}
}
}
}
}
}
@ -81,46 +36,32 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
}
bb0: {
StorageLive(_10);
StorageLive(_11);
StorageLive(_9);
StorageLive(_6);
StorageLive(_7);
StorageLive(_5);
StorageLive(_4);
StorageLive(_3);
_3 = const 31_u32;
_4 = BitAnd(_2, move _3);
StorageDead(_3);
StorageLive(_8);
StorageLive(_7);
StorageLive(_5);
_5 = Result::<u32, Infallible>::Ok(_4);
StorageLive(_6);
_6 = move ((_5 as Ok).0: u32);
_7 = Option::<u32>::Some(move _6);
StorageDead(_6);
StorageDead(_5);
StorageLive(_13);
_8 = move ((_7 as Some).0: u32);
StorageDead(_13);
StorageDead(_7);
_9 = unchecked_shl::<u32>(_1, move _8) -> [return: bb1, unwind unreachable];
_5 = unchecked_shl::<u32>(_1, _4) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_8);
StorageDead(_4);
_10 = Ge(_2, const _);
_11 = move _9;
StorageDead(_9);
StorageLive(_12);
_12 = unlikely(_10) -> [return: bb2, unwind unreachable];
_6 = Ge(_2, const _);
_7 = move _5;
StorageDead(_5);
StorageLive(_8);
_8 = unlikely(_6) -> [return: bb2, unwind unreachable];
}
bb2: {
switchInt(move _12) -> [0: bb3, otherwise: bb4];
switchInt(move _8) -> [0: bb3, otherwise: bb4];
}
bb3: {
_0 = Option::<u32>::Some(_11);
_0 = Option::<u32>::Some(_7);
goto -> bb5;
}
@ -130,9 +71,9 @@ fn checked_shl(_1: u32, _2: u32) -> Option<u32> {
}
bb5: {
StorageDead(_12);
StorageDead(_11);
StorageDead(_10);
StorageDead(_8);
StorageDead(_7);
StorageDead(_6);
return;
}
}