From 1139ee32aa803401198c02d5541accbf3fddfc94 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sat, 2 Oct 2021 12:16:08 +0200 Subject: [PATCH] Use bitand when checking for signed integer division overflow For `self == Self::MIN && rhs == -1`, LLVM does not realize that this is the same check made by `self / rhs`, so the code generated may have some unnecessary duplication. For `(self == Self::MIN) & (rhs == -1)`, LLVM realizes it is the same check. --- library/core/src/num/int_macros.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index daef5c98967cc..526e0c371882c 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -543,7 +543,8 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_div(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!(rhs == 0 || ((self == Self::MIN) & (rhs == -1))) { None } else { // SAFETY: div by zero and by INT_MIN have been checked above @@ -569,7 +570,8 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!(rhs == 0 || ((self == Self::MIN) & (rhs == -1))) { None } else { Some(self.div_euclid(rhs)) @@ -595,7 +597,8 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_rem(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!(rhs == 0 || ((self == Self::MIN) & (rhs == -1))) { None } else { // SAFETY: div by zero and by INT_MIN have been checked above @@ -621,7 +624,8 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!(rhs == 0 || ((self == Self::MIN) & (rhs == -1))) { None } else { Some(self.rem_euclid(rhs)) @@ -1466,7 +1470,8 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!((self == Self::MIN) & (rhs == -1)) { (self, true) } else { (self / rhs, false) @@ -1496,7 +1501,8 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!((self == Self::MIN) & (rhs == -1)) { (self, true) } else { (self.div_euclid(rhs), false) @@ -1527,7 +1533,8 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!((self == Self::MIN) & (rhs == -1)) { (0, true) } else { (self % rhs, false) @@ -1558,7 +1565,8 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { + // Using `&` helps LLVM see that it is the same check made in division. + if unlikely!((self == Self::MIN) & (rhs == -1)) { (0, true) } else { (self.rem_euclid(rhs), false)