[llvm] MathExtras: avoid unnecessarily widening types (PR #95426)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 26 14:11:32 PDT 2024


================
@@ -375,118 +397,151 @@ inline uint64_t PowerOf2Ceil(uint64_t A) {
   return UINT64_C(1) << Log2_64_Ceil(A);
 }
 
-/// Returns the next integer (mod 2**64) that is greater than or equal to
-/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
-///
-/// Examples:
-/// \code
-///   alignTo(5, 8) = 8
-///   alignTo(17, 8) = 24
-///   alignTo(~0LL, 8) = 0
-///   alignTo(321, 255) = 510
-/// \endcode
-///
-/// May overflow.
-inline uint64_t alignTo(uint64_t Value, uint64_t Align) {
-  assert(Align != 0u && "Align can't be 0.");
-  return (Value + Align - 1) / Align * Align;
-}
-
-inline uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
-  assert(Align != 0 && (Align & (Align - 1)) == 0 &&
-         "Align must be a power of 2");
-  // Replace unary minus to avoid compilation error on Windows:
-  // "unary minus operator applied to unsigned type, result still unsigned"
-  uint64_t negAlign = (~Align) + 1;
-  return (Value + Align - 1) & negAlign;
-}
-
-/// If non-zero \p Skew is specified, the return value will be a minimal integer
-/// that is greater than or equal to \p Size and equal to \p A * N + \p Skew for
-/// some integer N. If \p Skew is larger than \p A, its value is adjusted to '\p
-/// Skew mod \p A'. \p Align must be non-zero.
-///
-/// Examples:
-/// \code
-///   alignTo(5, 8, 7) = 7
-///   alignTo(17, 8, 1) = 17
-///   alignTo(~0LL, 8, 3) = 3
-///   alignTo(321, 255, 42) = 552
-/// \endcode
-inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew) {
-  assert(Align != 0u && "Align can't be 0.");
-  Skew %= Align;
-  return alignTo(Value - Skew, Align) + Skew;
-}
-
-/// Returns the next integer (mod 2**64) that is greater than or equal to
-/// \p Value and is a multiple of \c Align. \c Align must be non-zero.
-template <uint64_t Align> constexpr inline uint64_t alignTo(uint64_t Value) {
-  static_assert(Align != 0u, "Align must be non-zero");
-  return (Value + Align - 1) / Align * Align;
-}
-
 /// Returns the integer ceil(Numerator / Denominator). Unsigned version.
 /// Guaranteed to never overflow.
-inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) {
+template <typename U, typename V, typename T = common_uint<U, V>>
+constexpr T divideCeil(U Numerator, V Denominator) {
+  assert(Denominator && "Division by zero");
+  T Bias = (Numerator != 0);
+  return (Numerator - Bias) / Denominator + Bias;
+}
+
+/// Fallback when arguments aren't integral.
+constexpr uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) {
   assert(Denominator && "Division by zero");
   uint64_t Bias = (Numerator != 0);
   return (Numerator - Bias) / Denominator + Bias;
 }
 
 /// Returns the integer ceil(Numerator / Denominator). Signed version.
 /// Guaranteed to never overflow.
-inline int64_t divideCeilSigned(int64_t Numerator, int64_t Denominator) {
+template <typename U, typename V, typename T = common_sint<U, V>>
+constexpr T divideCeilSigned(U Numerator, V Denominator) {
   assert(Denominator && "Division by zero");
   if (!Numerator)
     return 0;
   // C's integer division rounds towards 0.
-  int64_t Bias = (Denominator >= 0 ? 1 : -1);
+  T Bias = Denominator >= 0 ? 1 : -1;
   bool SameSign = (Numerator >= 0) == (Denominator >= 0);
   return SameSign ? (Numerator - Bias) / Denominator + 1
                   : Numerator / Denominator;
 }
 
 /// Returns the integer floor(Numerator / Denominator). Signed version.
 /// Guaranteed to never overflow.
-inline int64_t divideFloorSigned(int64_t Numerator, int64_t Denominator) {
+template <typename U, typename V, typename T = common_sint<U, V>>
+constexpr T divideFloorSigned(U Numerator, V Denominator) {
   assert(Denominator && "Division by zero");
   if (!Numerator)
     return 0;
   // C's integer division rounds towards 0.
-  int64_t Bias = Denominator >= 0 ? -1 : 1;
+  T Bias = Denominator >= 0 ? -1 : 1;
   bool SameSign = (Numerator >= 0) == (Denominator >= 0);
   return SameSign ? Numerator / Denominator
                   : (Numerator - Bias) / Denominator - 1;
 }
 
 /// Returns the remainder of the Euclidean division of LHS by RHS. Result is
 /// always non-negative.
-inline int64_t mod(int64_t Numerator, int64_t Denominator) {
+template <typename U, typename V, typename T = common_sint<U, V>>
+constexpr T mod(U Numerator, V Denominator) {
   assert(Denominator >= 1 && "Mod by non-positive number");
-  int64_t Mod = Numerator % Denominator;
+  T Mod = Numerator % Denominator;
   return Mod < 0 ? Mod + Denominator : Mod;
 }
 
 /// Returns (Numerator / Denominator) rounded by round-half-up. Guaranteed to
 /// never overflow.
-inline uint64_t divideNearest(uint64_t Numerator, uint64_t Denominator) {
+template <typename U, typename V, typename T = common_uint<U, V>>
+constexpr T divideNearest(U Numerator, V Denominator) {
   assert(Denominator && "Division by zero");
-  uint64_t Mod = Numerator % Denominator;
-  return (Numerator / Denominator) + (Mod > (Denominator - 1) / 2);
+  T Mod = Numerator % Denominator;
+  return (Numerator / Denominator) +
+         (Mod > (static_cast<T>(Denominator) - 1) / 2);
 }
 
-/// Returns the largest uint64_t less than or equal to \p Value and is
-/// \p Skew mod \p Align. \p Align must be non-zero
-inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
+/// Returns the next integer (mod 2**nbits) that is greater than or equal to
+/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
+///
+/// Examples:
+/// \code
+///   alignTo(5, 8) = 8
+///   alignTo(17, 8) = 24
+///   alignTo(~0LL, 8) = 0
+///   alignTo(321, 255) = 510
+/// \endcode
+///
+/// Will overflow only if result is not representable in T.
+template <typename U, typename V, typename T = common_uint<U, V>>
+constexpr T alignTo(U Value, V Align) {
+  assert(Align != 0u && "Align can't be 0.");
+  T CeilDiv = divideCeil(Value, Align);
+  return CeilDiv * Align;
+}
+
+/// Fallback when arguments aren't integral.
+constexpr uint64_t alignTo(uint64_t Value, uint64_t Align) {
+  assert(Align != 0u && "Align can't be 0.");
+  uint64_t CeilDiv = divideCeil(Value, Align);
+  return CeilDiv * Align;
+}
+
+constexpr uint64_t alignToPowerOf2(uint64_t Value, uint64_t Align) {
----------------
artagnon wrote:

The callers are depending on implicit widening.

https://github.com/llvm/llvm-project/pull/95426


More information about the llvm-commits mailing list