[llvm] 7b44629 - [Support] Add constexpr versions of Add/Sub/MulOverflow (#210404)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 18 06:22:24 PDT 2026


Author: Krzysztof Parzyszek
Date: 2026-07-18T08:22:16-05:00
New Revision: 7b446294e098c7c02bbc998da1b4a438f7270879

URL: https://github.com/llvm/llvm-project/commit/7b446294e098c7c02bbc998da1b4a438f7270879
DIFF: https://github.com/llvm/llvm-project/commit/7b446294e098c7c02bbc998da1b4a438f7270879.diff

LOG: [Support] Add constexpr versions of Add/Sub/MulOverflow (#210404)

<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>

Added: 
    

Modified: 
    llvm/include/llvm/Support/MathExtras.h
    llvm/unittests/Support/MathExtrasTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
index f5383e3e63e8b..d429f63520616 100644
--- a/llvm/include/llvm/Support/MathExtras.h
+++ b/llvm/include/llvm/Support/MathExtras.h
@@ -22,6 +22,7 @@
 #include <cstring>
 #include <limits>
 #include <type_traits>
+#include <utility>
 
 namespace llvm {
 /// Some template parameter helpers to optimize for bitwidth, for functions that
@@ -696,12 +697,11 @@ SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
 LLVM_ABI extern const float huge_valf;
 
 /// Add two signed integers, computing the two's complement truncated result,
-/// returning true if overflow occurred.
+/// returning a pair {result, overflow}, where "overflow" is a boolean value
+/// indicating whether an overflow occurred.
 template <typename T>
-std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
-#if __has_builtin(__builtin_add_overflow)
-  return __builtin_add_overflow(X, Y, &Result);
-#else
+constexpr std::enable_if_t<std::is_signed_v<T>, std::pair<T, bool>>
+AddOverflow(T X, T Y) {
   // Perform the unsigned addition.
   using U = std::make_unsigned_t<T>;
   const U UX = static_cast<U>(X);
@@ -709,25 +709,36 @@ std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
   const U UResult = UX + UY;
 
   // Convert to signed.
-  Result = static_cast<T>(UResult);
+  auto Result = static_cast<T>(UResult);
 
   // Adding two positive numbers should result in a positive number.
   if (X > 0 && Y > 0)
-    return Result <= 0;
+    return {Result, Result <= 0};
   // Adding two negatives should result in a negative number.
   if (X < 0 && Y < 0)
-    return Result >= 0;
-  return false;
+    return {Result, Result >= 0};
+  return {Result, false};
+}
+
+/// Add two signed integers, computing the two's complement truncated result,
+/// returning true if overflow occurred.
+template <typename T>
+std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
+#if __has_builtin(__builtin_add_overflow)
+  return __builtin_add_overflow(X, Y, &Result);
+#else
+  auto [Res, Ovf] = AddOverflow(X, Y);
+  Result = Res;
+  return Ovf;
 #endif
 }
 
 /// Subtract two signed integers, computing the two's complement truncated
-/// result, returning true if an overflow occurred.
+/// result, returning a pair {result, overflow}, where "overflow" is a
+/// boolean value indicating whether an overflow occurred.
 template <typename T>
-std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
-#if __has_builtin(__builtin_sub_overflow)
-  return __builtin_sub_overflow(X, Y, &Result);
-#else
+constexpr std::enable_if_t<std::is_signed_v<T>, std::pair<T, bool>>
+SubOverflow(T X, T Y) {
   // Perform the unsigned addition.
   using U = std::make_unsigned_t<T>;
   const U UX = static_cast<U>(X);
@@ -735,25 +746,36 @@ std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
   const U UResult = UX - UY;
 
   // Convert to signed.
-  Result = static_cast<T>(UResult);
+  auto Result = static_cast<T>(UResult);
 
   // Subtracting a positive number from a negative results in a negative number.
   if (X <= 0 && Y > 0)
-    return Result >= 0;
+    return {Result, Result >= 0};
   // Subtracting a negative number from a positive results in a positive number.
   if (X >= 0 && Y < 0)
-    return Result <= 0;
-  return false;
-#endif
+    return {Result, Result <= 0};
+  return {Result, false};
 }
 
-/// Multiply two signed integers, computing the two's complement truncated
+/// Subtract two signed integers, computing the two's complement truncated
 /// result, returning true if an overflow occurred.
 template <typename T>
-std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
-#if __has_builtin(__builtin_mul_overflow)
-  return __builtin_mul_overflow(X, Y, &Result);
+std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
+#if __has_builtin(__builtin_sub_overflow)
+  return __builtin_sub_overflow(X, Y, &Result);
 #else
+  auto [Res, Ovf] = SubOverflow(X, Y);
+  Result = Res;
+  return Ovf;
+#endif
+}
+
+/// Multiply two signed integers, computing the two's complement truncated
+/// result, returning a pair {result, overflow}, where "overflow" is a
+/// boolean value indicating whether an overflow occurred.
+template <typename T>
+constexpr std::enable_if_t<std::is_signed_v<T>, std::pair<T, bool>>
+MulOverflow(T X, T Y) {
   // Perform the unsigned multiplication on absolute values.
   using U = std::make_unsigned_t<T>;
   const U UX = X < 0 ? (0 - static_cast<U>(X)) : static_cast<U>(X);
@@ -762,19 +784,33 @@ std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
 
   // Convert to signed.
   const bool IsNegative = (X < 0) ^ (Y < 0);
-  Result = IsNegative ? (0 - UResult) : UResult;
+  auto Result = IsNegative ? (0 - UResult) : UResult;
 
   // If any of the args was 0, result is 0 and no overflow occurs.
   if (UX == 0 || UY == 0)
-    return false;
+    return {Result, false};
 
   // UX and UY are in [1, 2^n], where n is the number of digits.
   // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
   // positive) divided by an argument compares to the other.
-  if (IsNegative)
-    return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY;
-  else
-    return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
+  bool Overflow =
+      IsNegative
+          ? UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY
+          : UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
+
+  return {Result, Overflow};
+}
+
+/// Multiply two signed integers, computing the two's complement truncated
+/// result, returning true if an overflow occurred.
+template <typename T>
+std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
+#if __has_builtin(__builtin_mul_overflow)
+  return __builtin_mul_overflow(X, Y, &Result);
+#else
+  auto [Res, Ovf] = MulOverflow(X, Y);
+  Result = Res;
+  return Ovf;
 #endif
 }
 

diff  --git a/llvm/unittests/Support/MathExtrasTest.cpp b/llvm/unittests/Support/MathExtrasTest.cpp
index 27e8b26e8801d..2e4f0a1ba7380 100644
--- a/llvm/unittests/Support/MathExtrasTest.cpp
+++ b/llvm/unittests/Support/MathExtrasTest.cpp
@@ -9,6 +9,7 @@
 #include "llvm/Support/MathExtras.h"
 #include "gtest/gtest.h"
 #include <limits>
+#include <utility>
 
 using namespace llvm;
 
@@ -693,6 +694,136 @@ TYPED_TEST(OverflowTest, MulResultZero) {
   EXPECT_EQ(Result, TypeParam(0));
 }
 
+template <typename T> class ConstexprOverflowTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE(ConstexprOverflowTest, OverflowTestTypes, );
+
+TYPED_TEST(ConstexprOverflowTest, AddNoOverflow) {
+  auto [Result, Overflow] = AddOverflow<TypeParam>(1, 2);
+  EXPECT_EQ(Result, TypeParam(3));
+  EXPECT_FALSE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, AddOverflowToNegative) {
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto [Result, Overflow] = AddOverflow<TypeParam>(MaxValue, MaxValue);
+  EXPECT_EQ(Result, TypeParam(-2));
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, AddOverflowToMin) {
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto [Result, Overflow] = AddOverflow<TypeParam>(MaxValue, TypeParam(1));
+  EXPECT_EQ(Result, std::numeric_limits<TypeParam>::min());
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, AddOverflowToZero) {
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = AddOverflow<TypeParam>(MinValue, MinValue);
+  EXPECT_EQ(Result, TypeParam(0));
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, AddOverflowToMax) {
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = AddOverflow<TypeParam>(MinValue, TypeParam(-1));
+  EXPECT_EQ(Result, std::numeric_limits<TypeParam>::max());
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, SubNoOverflow) {
+  auto [Result, Overflow] = SubOverflow<TypeParam>(1, 2);
+  EXPECT_EQ(Result, TypeParam(-1));
+  EXPECT_FALSE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, SubOverflowToMax) {
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = SubOverflow<TypeParam>(0, MinValue);
+  EXPECT_EQ(Result, MinValue);
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, SubOverflowToMin) {
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = SubOverflow<TypeParam>(0, MinValue);
+  EXPECT_EQ(Result, MinValue);
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, SubOverflowToNegative) {
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = SubOverflow<TypeParam>(MaxValue, MinValue);
+  EXPECT_EQ(Result, TypeParam(-1));
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, SubOverflowToPositive) {
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = SubOverflow<TypeParam>(MinValue, MaxValue);
+  EXPECT_EQ(Result, TypeParam(1));
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, MulNoOverflow) {
+  auto [Result1, Overflow1] = MulOverflow<TypeParam>(1, 2);
+  EXPECT_EQ(Result1, 2);
+  EXPECT_FALSE(Overflow1);
+  auto [Result2, Overflow2] = MulOverflow<TypeParam>(-1, 3);
+  EXPECT_EQ(Result2, -3);
+  EXPECT_FALSE(Overflow2);
+  auto [Result3, Overflow3] = MulOverflow<TypeParam>(4, -2);
+  EXPECT_EQ(Result3, -8);
+  EXPECT_FALSE(Overflow3);
+  auto [Result4, Overflow4] = MulOverflow<TypeParam>(-6, -5);
+  EXPECT_EQ(Result4, 30);
+  EXPECT_FALSE(Overflow4);
+}
+
+TYPED_TEST(ConstexprOverflowTest, MulNoOverflowToMax) {
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = MulOverflow<TypeParam>(MinValue + 1, -1);
+  EXPECT_EQ(Result, MaxValue);
+  EXPECT_FALSE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, MulOverflowToMin) {
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto [Result, Overflow] = MulOverflow<TypeParam>(MinValue, -1);
+  EXPECT_EQ(Result, MinValue);
+  EXPECT_TRUE(Overflow);
+}
+
+TYPED_TEST(ConstexprOverflowTest, MulOverflowMax) {
+  auto MinValue = std::numeric_limits<TypeParam>::min();
+  auto MaxValue = std::numeric_limits<TypeParam>::max();
+  auto [Result1, Overflow1] = MulOverflow<TypeParam>(MinValue, MinValue);
+  EXPECT_EQ(Result1, 0);
+  EXPECT_TRUE(Overflow1);
+  auto [Result2, Overflow2] = MulOverflow<TypeParam>(MaxValue, MaxValue);
+  EXPECT_EQ(Result2, 1);
+  EXPECT_TRUE(Overflow2);
+}
+
+TYPED_TEST(ConstexprOverflowTest, MulResultZero) {
+  auto [Result1, Overflow1] = MulOverflow<TypeParam>(4, 0);
+  EXPECT_EQ(Result1, TypeParam(0));
+  EXPECT_FALSE(Overflow1);
+  auto [Result2, Overflow2] = MulOverflow<TypeParam>(-5, 0);
+  EXPECT_EQ(Result2, TypeParam(0));
+  EXPECT_FALSE(Overflow2);
+  auto [Result3, Overflow3] = MulOverflow<TypeParam>(0, 5);
+  EXPECT_EQ(Result3, TypeParam(0));
+  EXPECT_FALSE(Overflow3);
+  auto [Result4, Overflow4] = MulOverflow<TypeParam>(0, -5);
+  EXPECT_EQ(Result4, TypeParam(0));
+  EXPECT_FALSE(Overflow4);
+}
+
 TEST(MathExtras, NumDigitsBase10) {
   EXPECT_EQ(NumDigitsBase10(0), 1);
   EXPECT_EQ(NumDigitsBase10(1), 1);


        


More information about the llvm-commits mailing list