Skip to content

Commit 3b16c04

Browse files
committed
unchecked_{shl|shr} should use u32 as the RHS
1 parent 1ca6777 commit 3b16c04

File tree

5 files changed

+87
-12
lines changed

5 files changed

+87
-12
lines changed

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
#![feature(const_pin)]
131131
#![feature(const_ptr_sub_ptr)]
132132
#![feature(const_replace)]
133+
#![feature(const_result_drop)]
133134
#![feature(const_ptr_as_ref)]
134135
#![feature(const_ptr_is_null)]
135136
#![feature(const_ptr_read)]

library/core/src/num/int_macros.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -757,10 +757,11 @@ macro_rules! int_impl {
757757
#[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
758758
#[inline(always)]
759759
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
760-
pub const unsafe fn unchecked_shl(self, rhs: Self) -> Self {
760+
pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
761761
// SAFETY: the caller must uphold the safety contract for
762762
// `unchecked_shl`.
763-
unsafe { intrinsics::unchecked_shl(self, rhs) }
763+
// Any legal shift amount is losslessly representable in the self type.
764+
unsafe { intrinsics::unchecked_shl(self, rhs.try_into().ok().unwrap_unchecked()) }
764765
}
765766

766767
/// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is
@@ -804,10 +805,11 @@ macro_rules! int_impl {
804805
#[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
805806
#[inline(always)]
806807
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
807-
pub const unsafe fn unchecked_shr(self, rhs: Self) -> Self {
808+
pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
808809
// SAFETY: the caller must uphold the safety contract for
809810
// `unchecked_shr`.
810-
unsafe { intrinsics::unchecked_shr(self, rhs) }
811+
// Any legal shift amount is losslessly representable in the self type.
812+
unsafe { intrinsics::unchecked_shr(self, rhs.try_into().ok().unwrap_unchecked()) }
811813
}
812814

813815
/// Checked absolute value. Computes `self.abs()`, returning `None` if
@@ -1354,11 +1356,12 @@ macro_rules! int_impl {
13541356
#[must_use = "this returns the result of the operation, \
13551357
without modifying the original"]
13561358
#[inline(always)]
1359+
#[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
13571360
pub const fn wrapping_shl(self, rhs: u32) -> Self {
13581361
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
13591362
// out of bounds
13601363
unsafe {
1361-
intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT)
1364+
self.unchecked_shl(rhs & ($BITS - 1))
13621365
}
13631366
}
13641367

@@ -1383,11 +1386,12 @@ macro_rules! int_impl {
13831386
#[must_use = "this returns the result of the operation, \
13841387
without modifying the original"]
13851388
#[inline(always)]
1389+
#[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
13861390
pub const fn wrapping_shr(self, rhs: u32) -> Self {
13871391
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
13881392
// out of bounds
13891393
unsafe {
1390-
intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT)
1394+
self.unchecked_shr(rhs & ($BITS - 1))
13911395
}
13921396
}
13931397

library/core/src/num/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![stable(feature = "rust1", since = "1.0.0")]
44

55
use crate::ascii;
6+
use crate::convert::TryInto;
67
use crate::error::Error;
78
use crate::intrinsics;
89
use crate::mem;

library/core/src/num/uint_macros.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -901,10 +901,11 @@ macro_rules! uint_impl {
901901
#[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
902902
#[inline(always)]
903903
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
904-
pub const unsafe fn unchecked_shl(self, rhs: Self) -> Self {
904+
pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
905905
// SAFETY: the caller must uphold the safety contract for
906906
// `unchecked_shl`.
907-
unsafe { intrinsics::unchecked_shl(self, rhs) }
907+
// Any legal shift amount is losslessly representable in the self type.
908+
unsafe { intrinsics::unchecked_shl(self, rhs.try_into().ok().unwrap_unchecked()) }
908909
}
909910

910911
/// Checked shift right. Computes `self >> rhs`, returning `None`
@@ -948,10 +949,11 @@ macro_rules! uint_impl {
948949
#[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
949950
#[inline(always)]
950951
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
951-
pub const unsafe fn unchecked_shr(self, rhs: Self) -> Self {
952+
pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
952953
// SAFETY: the caller must uphold the safety contract for
953954
// `unchecked_shr`.
954-
unsafe { intrinsics::unchecked_shr(self, rhs) }
955+
// Any legal shift amount is losslessly representable in the self type.
956+
unsafe { intrinsics::unchecked_shr(self, rhs.try_into().ok().unwrap_unchecked()) }
955957
}
956958

957959
/// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if
@@ -1367,11 +1369,12 @@ macro_rules! uint_impl {
13671369
#[must_use = "this returns the result of the operation, \
13681370
without modifying the original"]
13691371
#[inline(always)]
1372+
#[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
13701373
pub const fn wrapping_shl(self, rhs: u32) -> Self {
13711374
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
13721375
// out of bounds
13731376
unsafe {
1374-
intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT)
1377+
self.unchecked_shl(rhs & ($BITS - 1))
13751378
}
13761379
}
13771380

@@ -1399,11 +1402,12 @@ macro_rules! uint_impl {
13991402
#[must_use = "this returns the result of the operation, \
14001403
without modifying the original"]
14011404
#[inline(always)]
1405+
#[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
14021406
pub const fn wrapping_shr(self, rhs: u32) -> Self {
14031407
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
14041408
// out of bounds
14051409
unsafe {
1406-
intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT)
1410+
self.unchecked_shr(rhs & ($BITS - 1))
14071411
}
14081412
}
14091413

src/test/codegen/unchecked_shifts.rs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// compile-flags: -O
2+
// min-llvm-version: 15.0 (LLVM 13 in CI does this differently from submodule LLVM)
3+
4+
#![crate_type = "lib"]
5+
#![feature(unchecked_math)]
6+
7+
// CHECK-LABEL: @unchecked_shl_unsigned_same
8+
#[no_mangle]
9+
pub unsafe fn unchecked_shl_unsigned_same(a: u32, b: u32) -> u32 {
10+
// CHECK-NOT: and i32
11+
// CHECK: shl i32 %a, %b
12+
// CHECK-NOT: and i32
13+
a.unchecked_shl(b)
14+
}
15+
16+
// CHECK-LABEL: @unchecked_shl_unsigned_smaller
17+
#[no_mangle]
18+
pub unsafe fn unchecked_shl_unsigned_smaller(a: u16, b: u32) -> u16 {
19+
// This uses -DAG to avoid failing on irrelevant reorderings,
20+
// like emitting the truncation earlier.
21+
22+
// CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 65536
23+
// CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]])
24+
// CHECK-DAG: %[[TRUNC:.+]] = trunc i32 %b to i16
25+
// CHECK-DAG: shl i16 %a, %[[TRUNC]]
26+
a.unchecked_shl(b)
27+
}
28+
29+
// CHECK-LABEL: @unchecked_shl_unsigned_bigger
30+
#[no_mangle]
31+
pub unsafe fn unchecked_shl_unsigned_bigger(a: u64, b: u32) -> u64 {
32+
// CHECK: %[[EXT:.+]] = zext i32 %b to i64
33+
// CHECK: shl i64 %a, %[[EXT]]
34+
a.unchecked_shl(b)
35+
}
36+
37+
// CHECK-LABEL: @unchecked_shr_signed_same
38+
#[no_mangle]
39+
pub unsafe fn unchecked_shr_signed_same(a: i32, b: u32) -> i32 {
40+
// CHECK-NOT: and i32
41+
// CHECK: ashr i32 %a, %b
42+
// CHECK-NOT: and i32
43+
a.unchecked_shr(b)
44+
}
45+
46+
// CHECK-LABEL: @unchecked_shr_signed_smaller
47+
#[no_mangle]
48+
pub unsafe fn unchecked_shr_signed_smaller(a: i16, b: u32) -> i16 {
49+
// This uses -DAG to avoid failing on irrelevant reorderings,
50+
// like emitting the truncation earlier.
51+
52+
// CHECK-DAG: %[[INRANGE:.+]] = icmp ult i32 %b, 32768
53+
// CHECK-DAG: tail call void @llvm.assume(i1 %[[INRANGE]])
54+
// CHECK-DAG: %[[TRUNC:.+]] = trunc i32 %b to i16
55+
// CHECK-DAG: ashr i16 %a, %[[TRUNC]]
56+
a.unchecked_shr(b)
57+
}
58+
59+
// CHECK-LABEL: @unchecked_shr_signed_bigger
60+
#[no_mangle]
61+
pub unsafe fn unchecked_shr_signed_bigger(a: i64, b: u32) -> i64 {
62+
// CHECK: %[[EXT:.+]] = zext i32 %b to i64
63+
// CHECK: ashr i64 %a, %[[EXT]]
64+
a.unchecked_shr(b)
65+
}

0 commit comments

Comments
 (0)