Skip to content

Commit 2a4abc3

Browse files
committed
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.
1 parent c628501 commit 2a4abc3

File tree

2 files changed

+47
-10
lines changed

2 files changed

+47
-10
lines changed

core/src/num/int_macros.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -1163,12 +1163,19 @@ macro_rules! int_impl {
11631163
/// ```
11641164
#[stable(feature = "wrapping", since = "1.7.0")]
11651165
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
1166+
// We could always go back to wrapping
1167+
#[rustc_allow_const_fn_unstable(unchecked_shifts)]
11661168
#[must_use = "this returns the result of the operation, \
11671169
without modifying the original"]
11681170
#[inline]
11691171
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
1170-
let (a, b) = self.overflowing_shl(rhs);
1171-
if unlikely!(b) { None } else { Some(a) }
1172+
// Not using overflowing_shl as that's a wrapping shift
1173+
if rhs < Self::BITS {
1174+
// SAFETY: just checked the RHS is in-range
1175+
Some(unsafe { self.unchecked_shl(rhs) })
1176+
} else {
1177+
None
1178+
}
11721179
}
11731180

11741181
/// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
@@ -1254,12 +1261,19 @@ macro_rules! int_impl {
12541261
/// ```
12551262
#[stable(feature = "wrapping", since = "1.7.0")]
12561263
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
1264+
// We could always go back to wrapping
1265+
#[rustc_allow_const_fn_unstable(unchecked_shifts)]
12571266
#[must_use = "this returns the result of the operation, \
12581267
without modifying the original"]
12591268
#[inline]
12601269
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
1261-
let (a, b) = self.overflowing_shr(rhs);
1262-
if unlikely!(b) { None } else { Some(a) }
1270+
// Not using overflowing_shr as that's a wrapping shift
1271+
if rhs < Self::BITS {
1272+
// SAFETY: just checked the RHS is in-range
1273+
Some(unsafe { self.unchecked_shr(rhs) })
1274+
} else {
1275+
None
1276+
}
12631277
}
12641278

12651279
/// Strict shift right. Computes `self >> rhs`, panicking `rhs` is

core/src/num/uint_macros.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -579,8 +579,17 @@ macro_rules! uint_impl {
579579
without modifying the original"]
580580
#[inline]
581581
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
582-
let (a, b) = self.overflowing_sub(rhs);
583-
if unlikely!(b) { None } else { Some(a) }
582+
// Per PR#103299, there's no advantage to the `overflowing` intrinsic
583+
// for *unsigned* subtraction and we just emit the manual check anyway.
584+
// Thus, rather than using `overflowing_sub` that produces a wrapping
585+
// subtraction, check it ourself so we can use an unchecked one.
586+
587+
if self >= rhs {
588+
// SAFETY: just checked this can't overflow
589+
Some(unsafe { intrinsics::unchecked_sub(self, rhs) })
590+
} else {
591+
None
592+
}
584593
}
585594

586595
/// Strict integer subtraction. Computes `self - rhs`, panicking if
@@ -1222,12 +1231,19 @@ macro_rules! uint_impl {
12221231
/// ```
12231232
#[stable(feature = "wrapping", since = "1.7.0")]
12241233
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
1234+
// We could always go back to wrapping
1235+
#[rustc_allow_const_fn_unstable(unchecked_shifts)]
12251236
#[must_use = "this returns the result of the operation, \
12261237
without modifying the original"]
12271238
#[inline]
12281239
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
1229-
let (a, b) = self.overflowing_shl(rhs);
1230-
if unlikely!(b) { None } else { Some(a) }
1240+
// Not using overflowing_shl as that's a wrapping shift
1241+
if rhs < Self::BITS {
1242+
// SAFETY: just checked the RHS is in-range
1243+
Some(unsafe { self.unchecked_shl(rhs) })
1244+
} else {
1245+
None
1246+
}
12311247
}
12321248

12331249
/// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
@@ -1313,12 +1329,19 @@ macro_rules! uint_impl {
13131329
/// ```
13141330
#[stable(feature = "wrapping", since = "1.7.0")]
13151331
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
1332+
// We could always go back to wrapping
1333+
#[rustc_allow_const_fn_unstable(unchecked_shifts)]
13161334
#[must_use = "this returns the result of the operation, \
13171335
without modifying the original"]
13181336
#[inline]
13191337
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
1320-
let (a, b) = self.overflowing_shr(rhs);
1321-
if unlikely!(b) { None } else { Some(a) }
1338+
// Not using overflowing_shr as that's a wrapping shift
1339+
if rhs < Self::BITS {
1340+
// SAFETY: just checked the RHS is in-range
1341+
Some(unsafe { self.unchecked_shr(rhs) })
1342+
} else {
1343+
None
1344+
}
13221345
}
13231346

13241347
/// Strict shift right. Computes `self >> rhs`, panicking `rhs` is

0 commit comments

Comments
 (0)