summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorScott McMurray <scottmcm@users.noreply.github.com>2024-04-17 23:55:35 -0700
committerScott McMurray <scottmcm@users.noreply.github.com>2024-04-18 18:11:21 -0700
commit986d9f104b2da25f7388ad824b9868c3ce1e5f21 (patch)
tree7ac1d3527e66ce610f6a69835bc5369f8334ff14 /library
parente3181b091e88321f5ea149afed6db0edf0a4f37b (diff)
Make `checked` ops emit *unchecked* LLVM operations where feasible
For things with easily pre-checked overflow conditions -- shifts and unsigned subtraction -- write then checked methods in such a way that we stop emitting wrapping versions of them. For example, today <https://rust.godbolt.org/z/qM9YK8Txb> neither ```rust a.checked_sub(b).unwrap() ``` nor ```rust a.checked_sub(b).unwrap_unchecked() ``` actually optimizes to `sub nuw`. After this PR they do.
Diffstat (limited to 'library')
-rw-r--r--library/core/src/num/int_macros.rs22
-rw-r--r--library/core/src/num/uint_macros.rs35
2 files changed, 47 insertions, 10 deletions
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index e34e9b7fff6..b6aab9be358 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -1163,12 +1163,19 @@ macro_rules! int_impl {
/// ```
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+ // We could always go back to wrapping
+ #[rustc_allow_const_fn_unstable(unchecked_shifts)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
- let (a, b) = self.overflowing_shl(rhs);
- if unlikely!(b) { None } else { Some(a) }
+ // Not using overflowing_shl as that's a wrapping shift
+ if rhs < Self::BITS {
+ // SAFETY: just checked the RHS is in-range
+ Some(unsafe { self.unchecked_shl(rhs) })
+ } else {
+ None
+ }
}
/// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
@@ -1254,12 +1261,19 @@ macro_rules! int_impl {
/// ```
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+ // We could always go back to wrapping
+ #[rustc_allow_const_fn_unstable(unchecked_shifts)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
- let (a, b) = self.overflowing_shr(rhs);
- if unlikely!(b) { None } else { Some(a) }
+ // Not using overflowing_shr as that's a wrapping shift
+ if rhs < Self::BITS {
+ // SAFETY: just checked the RHS is in-range
+ Some(unsafe { self.unchecked_shr(rhs) })
+ } else {
+ None
+ }
}
/// Strict shift right. Computes `self >> rhs`, panicking `rhs` is
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index ba6a243041c..58f713baa57 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -579,8 +579,17 @@ macro_rules! uint_impl {
without modifying the original"]
#[inline]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
- let (a, b) = self.overflowing_sub(rhs);
- if unlikely!(b) { None } else { Some(a) }
+ // Per PR#103299, there's no advantage to the `overflowing` intrinsic
+ // for *unsigned* subtraction and we just emit the manual check anyway.
+ // Thus, rather than using `overflowing_sub` that produces a wrapping
+ // subtraction, check it ourself so we can use an unchecked one.
+
+ if self >= rhs {
+ // SAFETY: just checked this can't overflow
+ Some(unsafe { intrinsics::unchecked_sub(self, rhs) })
+ } else {
+ None
+ }
}
/// Strict integer subtraction. Computes `self - rhs`, panicking if
@@ -1222,12 +1231,19 @@ macro_rules! uint_impl {
/// ```
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+ // We could always go back to wrapping
+ #[rustc_allow_const_fn_unstable(unchecked_shifts)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
- let (a, b) = self.overflowing_shl(rhs);
- if unlikely!(b) { None } else { Some(a) }
+ // Not using overflowing_shl as that's a wrapping shift
+ if rhs < Self::BITS {
+ // SAFETY: just checked the RHS is in-range
+ Some(unsafe { self.unchecked_shl(rhs) })
+ } else {
+ None
+ }
}
/// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
@@ -1313,12 +1329,19 @@ macro_rules! uint_impl {
/// ```
#[stable(feature = "wrapping", since = "1.7.0")]
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
+ // We could always go back to wrapping
+ #[rustc_allow_const_fn_unstable(unchecked_shifts)]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
- let (a, b) = self.overflowing_shr(rhs);
- if unlikely!(b) { None } else { Some(a) }
+ // Not using overflowing_shr as that's a wrapping shift
+ if rhs < Self::BITS {
+ // SAFETY: just checked the RHS is in-range
+ Some(unsafe { self.unchecked_shr(rhs) })
+ } else {
+ None
+ }
}
/// Strict shift right. Computes `self >> rhs`, panicking `rhs` is