[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