Skip to content

Commit

Permalink
Make overflow checks optional for unsigned integers
Browse files Browse the repository at this point in the history
If SUS_CHECK_INTEGER_OVERFLOW is defined to false then overflow checks
will be removed in unsigned integers. Wrapping operations will be
performed for shift operations instead of Undefined Behaviour.
  • Loading branch information
danakj committed Nov 29, 2023
1 parent 2ca796c commit 09a309f
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 121 deletions.
1 change: 1 addition & 0 deletions sus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ if(${SUBSPACE_BUILD_TESTS})
"num/overflow_integer_unittest.cc"
"num/u8_unittest.cc"
"num/u16_unittest.cc"
"num/u32_overflow_unittest.cc"
"num/u32_unittest.cc"
"num/u64_unittest.cc"
"num/uptr_unittest.cc"
Expand Down
8 changes: 4 additions & 4 deletions sus/num/__private/intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,8 +705,8 @@ sus_pure_const sus_always_inline constexpr T reverse_bits(T value) noexcept {

template <class T>
requires(std::is_integral_v<T> && std::is_unsigned_v<T>)
sus_pure_const inline constexpr T rotate_left(T value, uint32_t n) noexcept {
const uint32_t num_bits = unchecked_mul(unchecked_sizeof<T>(), uint32_t{8});
sus_pure_const inline constexpr T rotate_left(T value, uint64_t n) noexcept {
const uint64_t num_bits = unchecked_mul(unchecked_sizeof<T>(), uint32_t{8});
// Try avoid slow % operation if we can. Comparisons are much faster than %.
if (n >= num_bits) n %= num_bits;
if (n == 0) return value;
Expand All @@ -716,8 +716,8 @@ sus_pure_const inline constexpr T rotate_left(T value, uint32_t n) noexcept {

template <class T>
requires(std::is_integral_v<T> && std::is_unsigned_v<T>)
sus_pure_const inline constexpr T rotate_right(T value, uint32_t n) noexcept {
const uint32_t num_bits = unchecked_mul(unchecked_sizeof<T>(), uint32_t{8});
sus_pure_const inline constexpr T rotate_right(T value, uint64_t n) noexcept {
const uint64_t num_bits = unchecked_mul(unchecked_sizeof<T>(), uint32_t{8});
// Try avoid slow % operation if we can. Comparisons are much faster than %.
if (n >= num_bits) n %= num_bits;
if (n == 0) return value;
Expand Down
8 changes: 4 additions & 4 deletions sus/num/__private/signed_integer_methods.inc
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ constexpr inline void operator^=(_self r) & noexcept {
/// This operator will panic if the shift amount is not less than
/// [`@doc.self::BITS`]($sus::num::@doc.self::BITS) and [overflow checks](
/// $sus::num#overflow-behaviour) are enabled.
constexpr inline void operator<<=(u32 r) & noexcept {
constexpr inline void operator<<=(u64 r) & noexcept {
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::shl_with_overflow(primitive_value, r.primitive_value);
Expand All @@ -1016,7 +1016,7 @@ constexpr inline void operator<<=(u32 r) & noexcept {
/// This operator will panic if the shift amount is not less than
/// [`@doc.self::BITS`]($sus::num::@doc.self::BITS) and [overflow checks](
/// $sus::num#overflow-behaviour) are enabled.
constexpr inline void operator>>=(u32 r) & noexcept {
constexpr inline void operator>>=(u64 r) & noexcept {
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::shr_with_overflow(primitive_value, r.primitive_value);
Expand Down Expand Up @@ -1798,7 +1798,7 @@ sus_pure constexpr _self reverse_bits() const& noexcept {
/// truncated bits to the end of the resulting integer.
///
/// Please note this isn't the same operation as the `<<` shifting operator!
sus_pure constexpr _self rotate_left(u32 n) const& noexcept {
sus_pure constexpr _self rotate_left(u64 n) const& noexcept {
return __private::into_signed(__private::rotate_left(
__private::into_unsigned(primitive_value), n.primitive_value));
}
Expand All @@ -1807,7 +1807,7 @@ sus_pure constexpr _self rotate_left(u32 n) const& noexcept {
/// truncated bits to the beginning of the resulting integer.
///
/// Please note this isn't the same operation as the >> shifting operator!
sus_pure constexpr _self rotate_right(u32 n) const& noexcept {
sus_pure constexpr _self rotate_right(u64 n) const& noexcept {
return __private::into_signed(__private::rotate_right(
__private::into_unsigned(primitive_value), n.primitive_value));
}
Expand Down
120 changes: 61 additions & 59 deletions sus/num/__private/unsigned_integer_methods.inc
Original file line number Diff line number Diff line change
Expand Up @@ -627,11 +627,15 @@ sus_pure constexpr inline _self operator~() const& noexcept {
/// #[doc.overloads=unsignedint.+]
[[nodiscard]] sus_pure_const friend constexpr inline _self operator+(
_self l, _self r) noexcept {
const auto out =
__private::add_with_overflow(l.primitive_value, r.primitive_value);
// TODO: Allow opting out of all overflow checks?
::sus::check(!out.overflow);
return _self(out.value);
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::add_with_overflow(l.primitive_value, r.primitive_value);
::sus::check(!out.overflow);
return _self(out.value);
} else {
return _self(
__private::unchecked_add(l.primitive_value, r.primitive_value));
}
}
#if _pointer
/// #[doc.overloads=unsignedint.+]
Expand Down Expand Up @@ -689,11 +693,15 @@ friend constexpr inline _self operator+(U l, _self r) noexcept = delete;
/// #[doc.overloads=unsignedint.-]
[[nodiscard]] sus_pure_const friend constexpr inline _self operator-(
_self l, _self r) noexcept {
const auto out =
__private::sub_with_overflow(l.primitive_value, r.primitive_value);
// TODO: Allow opting out of all overflow checks?
::sus::check(!out.overflow);
return _self(out.value);
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::sub_with_overflow(l.primitive_value, r.primitive_value);
::sus::check(!out.overflow);
return _self(out.value);
} else {
return _self(
__private::unchecked_sub(l.primitive_value, r.primitive_value));
}
}
#if _pointer
/// #[doc.overloads=unsignedint.-]
Expand Down Expand Up @@ -751,11 +759,15 @@ friend constexpr inline _self operator-(U l, _self r) noexcept = delete;
/// #[doc.overloads=unsignedint.*]
[[nodiscard]] sus_pure_const friend constexpr inline _self operator*(
_self l, _self r) noexcept {
const auto out =
__private::mul_with_overflow(l.primitive_value, r.primitive_value);
// TODO: Allow opting out of all overflow checks?
::sus::check(!out.overflow);
return _self(out.value);
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::mul_with_overflow(l.primitive_value, r.primitive_value);
::sus::check(!out.overflow);
return _self(out.value);
} else {
return _self(
__private::unchecked_mul(l.primitive_value, r.primitive_value));
}
}
#if _pointer
/// #[doc.overloads=unsignedint.*]
Expand Down Expand Up @@ -813,7 +825,6 @@ friend constexpr inline _self operator*(U l, _self r) noexcept = delete;
/// #[doc.overloads=unsignedint./]
[[nodiscard]] sus_pure_const friend constexpr inline _self operator/(
_self l, _self r) noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(r.primitive_value != 0u);
return _self(__private::unchecked_div(l.primitive_value, r.primitive_value));
}
Expand Down Expand Up @@ -873,7 +884,6 @@ friend constexpr inline _self operator/(U l, _self r) noexcept = delete;
/// #[doc.overloads=unsignedint.%]
[[nodiscard]] sus_pure_const friend constexpr inline _self operator%(
_self l, _self r) noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(r.primitive_value != 0u);
return _self(__private::unchecked_rem(l.primitive_value, r.primitive_value));
}
Expand Down Expand Up @@ -1101,11 +1111,15 @@ friend constexpr inline _self operator^(U l, _self r) noexcept = delete;

/// Satisfies the [`AddAssign<@doc.self>`]($sus::num::AddAssign) concept.
constexpr inline void operator+=(_self r) & noexcept {
const auto out =
__private::add_with_overflow(primitive_value, r.primitive_value);
// TODO: Allow opting out of all overflow checks?
::sus::check(!out.overflow);
primitive_value = out.value;
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::add_with_overflow(primitive_value, r.primitive_value);
::sus::check(!out.overflow);
primitive_value = out.value;
} else {
primitive_value =
__private::unchecked_add(primitive_value, r.primitive_value);
}
}
#if _pointer // uptr can't be created from other safe numerics.
constexpr inline void operator+=(_pointer_sized r) & noexcept {
Expand All @@ -1114,11 +1128,15 @@ constexpr inline void operator+=(_pointer_sized r) & noexcept {
#endif
/// Satisfies the [`SubAssign<@doc.self>`]($sus::num::SubAssign) concept.
constexpr inline void operator-=(_self r) & noexcept {
const auto out =
__private::sub_with_overflow(primitive_value, r.primitive_value);
// TODO: Allow opting out of all overflow checks?
::sus::check(!out.overflow);
primitive_value = out.value;
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::sub_with_overflow(primitive_value, r.primitive_value);
::sus::check(!out.overflow);
primitive_value = out.value;
} else {
primitive_value =
__private::unchecked_sub(primitive_value, r.primitive_value);
}
}
#if _pointer // uptr can't be created from other safe numerics.
constexpr inline void operator-=(_pointer_sized r) & noexcept {
Expand All @@ -1127,11 +1145,15 @@ constexpr inline void operator-=(_pointer_sized r) & noexcept {
#endif
/// Satisfies the [`MulAssign<@doc.self>`]($sus::num::MulAssign) concept.
constexpr inline void operator*=(_self r) & noexcept {
const auto out =
__private::mul_with_overflow(primitive_value, r.primitive_value);
// TODO: Allow opting out of all overflow checks?
::sus::check(!out.overflow);
primitive_value = out.value;
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
const auto out =
__private::mul_with_overflow(primitive_value, r.primitive_value);
::sus::check(!out.overflow);
primitive_value = out.value;
} else {
primitive_value =
__private::unchecked_mul(primitive_value, r.primitive_value);
}
}
#if _pointer // uptr can't be created from other safe numerics.
constexpr inline void operator*=(_pointer_sized r) & noexcept {
Expand All @@ -1140,7 +1162,6 @@ constexpr inline void operator*=(_pointer_sized r) & noexcept {
#endif
/// Satisfies the [`DivAssign<@doc.self>`]($sus::num::DivAssign) concept.
constexpr inline void operator/=(_self r) & noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(r.primitive_value != 0u);
primitive_value /= r.primitive_value;
}
Expand All @@ -1151,7 +1172,6 @@ constexpr inline void operator/=(_pointer_sized r) & noexcept {
#endif
/// Satisfies the [`RemAssign<@doc.self>`]($sus::num::RemAssign) concept.
constexpr inline void operator%=(_self r) & noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(r.primitive_value != 0u);
primitive_value %= r.primitive_value;
}
Expand Down Expand Up @@ -1191,17 +1211,9 @@ constexpr inline void operator^=(_pointer_sized r) & noexcept {
}
#endif
/// Satisfies the [`ShlAssign<@doc.self>`]($sus::num::ShlAssign) concept.
constexpr inline void operator<<=(u32 r) & noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(r < u32(__private::num_bits<_primitive>()));
primitive_value <<= r.primitive_value;
}
constexpr inline void operator<<=(u64 r) & noexcept;
/// Satisfies the [`ShrAssign<@doc.self>`]($sus::num::ShrAssign) concept.
constexpr inline void operator>>=(u32 r) & noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(r < u32(__private::num_bits<_primitive>()));
primitive_value >>= r.primitive_value;
}
constexpr inline void operator>>=(u64 r) & noexcept;

/// Computes the absolute difference between `self` and `other`.
sus_pure constexpr _self abs_diff(_self other) const& noexcept {
Expand Down Expand Up @@ -1334,7 +1346,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_div(
/// # Panics
/// This function will panic if `rhs` is 0.
sus_pure constexpr _self saturating_div(_self rhs) const& noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(rhs.primitive_value != 0u);
return _self(__private::unchecked_div(primitive_value, rhs.primitive_value));
}
Expand All @@ -1347,7 +1358,6 @@ sus_pure constexpr _self saturating_div(_self rhs) const& noexcept {
/// # Panics
/// This function will panic if `rhs` is 0.
sus_pure constexpr _self wrapping_div(_self rhs) const& noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(rhs.primitive_value != 0u);
return _self(__private::unchecked_div(primitive_value, rhs.primitive_value));
}
Expand Down Expand Up @@ -1463,7 +1473,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_rem(
/// # Panics
/// This function will panic if `rhs` is 0.
sus_pure constexpr _self wrapping_rem(_self rhs) const& noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(rhs.primitive_value != 0u);
return _self(__private::unchecked_rem(primitive_value, rhs.primitive_value));
}
Expand All @@ -1476,7 +1485,6 @@ sus_pure constexpr _self wrapping_rem(_self rhs) const& noexcept {
/// # Panics
/// This function will panic if `rhs` is 0.
sus_pure constexpr _self div_euclid(_self rhs) const& noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(rhs.primitive_value != 0u);
return _self(__private::unchecked_div(primitive_value, rhs.primitive_value));
}
Expand Down Expand Up @@ -1512,7 +1520,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_div_euclid(
/// # Panics
/// This function will panic if `rhs` is 0.
sus_pure constexpr _self wrapping_div_euclid(_self rhs) const& noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(rhs.primitive_value != 0u);
return _self(__private::unchecked_div(primitive_value, rhs.primitive_value));
}
Expand All @@ -1525,7 +1532,6 @@ sus_pure constexpr _self wrapping_div_euclid(_self rhs) const& noexcept {
/// # Panics
/// This function will panic if `rhs` is 0.
sus_pure constexpr _self rem_euclid(_self rhs) const& noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(rhs.primitive_value != 0u);
return _self(__private::unchecked_rem(primitive_value, rhs.primitive_value));
}
Expand Down Expand Up @@ -1562,7 +1568,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_rem_euclid(
/// # Panics
/// This function will panic if `rhs` is 0.
sus_pure constexpr _self wrapping_rem_euclid(_self rhs) const& noexcept {
// TODO: Allow opting out of all overflow checks?
::sus::check(rhs.primitive_value != 0u);
return _self(__private::unchecked_rem(primitive_value, rhs.primitive_value));
}
Expand Down Expand Up @@ -1725,17 +1730,13 @@ sus_pure constexpr _self reverse_bits() const& noexcept {
/// truncated bits to the end of the resulting integer.
///
/// Please note this isn't the same operation as the `<<` shifting operator!
sus_pure constexpr _self rotate_left(u32 n) const& noexcept {
return _self(__private::rotate_left(primitive_value, n.primitive_value));
}
sus_pure constexpr _self rotate_left(u64 n) const& noexcept;

/// Shifts the bits to the right by a specified amount, n, wrapping the
/// truncated bits to the beginning of the resulting integer.
///
/// Please note this isn't the same operation as the >> shifting operator!
sus_pure constexpr _self rotate_right(u32 n) const& noexcept {
return _self(__private::rotate_right(primitive_value, n.primitive_value));
}
sus_pure constexpr _self rotate_right(u64 n) const& noexcept;

/// Reverses the byte order of the integer.
sus_pure constexpr _self swap_bytes() const& noexcept {
Expand All @@ -1746,8 +1747,9 @@ sus_pure constexpr _self swap_bytes() const& noexcept {
sus_pure constexpr _self pow(u32 rhs) const& noexcept {
const auto out =
__private::pow_with_overflow(primitive_value, rhs.primitive_value);
// TODO: Allow opting out of all overflow checks?
::sus::check(!out.overflow);
if constexpr (SUS_CHECK_INTEGER_OVERFLOW) {
::sus::check(!out.overflow);
}
return _self(out.value);
}

Expand Down
Loading

0 comments on commit 09a309f

Please sign in to comment.