[llvm] [MathExtras] Optimize SaturatingAdd. Split it into two specialized parst and use reference instead pointer for owerflow flag (PR #190479)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Apr 4 11:53:08 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Max Graey (MaxGraey)
<details>
<summary>Changes</summary>
Motivations:
- This improves the API and makes the code less verbose
- This significantly improves the quality of the generated code both on GCC and Clang for SaturatingAdd
**New**
--
Clang (trunk):
```asm
add rdi, rsi
setb byte ptr [rdx]
mov rax, -1
cmovae rax, rdi
ret
```
GCC (11.4)
```asm
add rdi, rsi
setc al
mov BYTE PTR [rdx], al
sal rax, 63
sar rax, 63
or rax, rdi
ret
```
**Old**
--
Clang (trunk):
```asm
add rsi, rdi
test rdx, rdx
je .LBB0_2
cmp rsi, rdi
setb byte ptr [rdx]
.LBB0_2:
cmp rsi, rdi
mov rax, -1
cmovae rax, rsi
ret
```
GCC (11.4)
```asm
lea rax, [rsp-1]
test rdx, rdx
cmove rdx, rax
cmp rdi, rsi
lea rax, [rdi+rsi]
cmovb rdi, rsi
cmp rax, rdi
setb BYTE PTR [rdx]
mov rdx, -1
cmovb rax, rdx
ret
```
See: https://godbolt.org/z/xY45ss678
---
Full diff: https://github.com/llvm/llvm-project/pull/190479.diff
3 Files Affected:
- (modified) llvm/include/llvm/Support/MathExtras.h (+20-16)
- (modified) llvm/lib/IR/DIExpressionOptimizer.cpp (+1-1)
- (modified) llvm/unittests/Support/MathExtrasTest.cpp (+14-14)
``````````diff
diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
index de2813094a950..bec1f4565700b 100644
--- a/llvm/include/llvm/Support/MathExtras.h
+++ b/llvm/include/llvm/Support/MathExtras.h
@@ -601,30 +601,34 @@ constexpr T AbsoluteDifference(U X, V Y) {
return X > Y ? (X - Y) : (Y - X);
}
-/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
-/// maximum representable value of T on overflow. ResultOverflowed indicates if
+/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
+/// maximum representable value of T on overflow.
+template <typename T>
+std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y) {
+ T Z = X + Y;
+ // Z < X and Z < Y are equivalent overflow checks, so one suffices
+ return Z | -(Z < X);
+}
+
+/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
+/// maximum representable value of T on overflow. Overflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
-std::enable_if_t<std::is_unsigned_v<T>, T>
-SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
- bool Dummy;
- bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
- // Hacker's Delight, p. 29
+std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y,
+ bool &Overflowed) {
T Z = X + Y;
- Overflowed = (Z < X || Z < Y);
- if (Overflowed)
- return std::numeric_limits<T>::max();
- else
- return Z;
+ // Z < X and Z < Y are equivalent overflow checks, so one suffices
+ Overflowed = (Z < X);
+ return Z | -Overflowed;
}
-/// Add multiple unsigned integers of type T. Clamp the result to the
+/// Add multiple unsigned integers of type T. Clamp the result to the
/// maximum representable value of T on overflow.
template <class T, class... Ts>
std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
Ts... Args) {
bool Overflowed = false;
- T XY = SaturatingAdd(X, Y, &Overflowed);
+ T XY = SaturatingAdd(X, Y, Overflowed);
if (Overflowed)
return SaturatingAdd(std::numeric_limits<T>::max(), T(1), Args...);
return SaturatingAdd(XY, Z, Args...);
@@ -670,7 +674,7 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
}
Z <<= 1;
if (X & 1)
- return SaturatingAdd(Z, Y, ResultOverflowed);
+ return SaturatingAdd(Z, Y, Overflowed);
return Z;
}
@@ -689,7 +693,7 @@ SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
if (Overflowed)
return Product;
- return SaturatingAdd(A, Product, &Overflowed);
+ return SaturatingAdd(A, Product, Overflowed);
}
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
diff --git a/llvm/lib/IR/DIExpressionOptimizer.cpp b/llvm/lib/IR/DIExpressionOptimizer.cpp
index be9e13a34235a..069991f60c08e 100644
--- a/llvm/lib/IR/DIExpressionOptimizer.cpp
+++ b/llvm/lib/IR/DIExpressionOptimizer.cpp
@@ -48,7 +48,7 @@ foldOperationIfPossible(uint64_t Const1, uint64_t Const2,
bool ResultOverflowed;
switch (Operator) {
case dwarf::DW_OP_plus: {
- auto Result = SaturatingAdd(Const1, Const2, &ResultOverflowed);
+ auto Result = SaturatingAdd(Const1, Const2, ResultOverflowed);
if (ResultOverflowed)
return std::nullopt;
return Result;
diff --git a/llvm/unittests/Support/MathExtrasTest.cpp b/llvm/unittests/Support/MathExtrasTest.cpp
index 27e8b26e8801d..add7a7e778666 100644
--- a/llvm/unittests/Support/MathExtrasTest.cpp
+++ b/llvm/unittests/Support/MathExtrasTest.cpp
@@ -237,59 +237,59 @@ template <typename T> void SaturatingAddTestHelper() {
bool ResultOverflowed;
EXPECT_EQ(T(3), SaturatingAdd(T(1), T(2)));
- EXPECT_EQ(T(3), SaturatingAdd(T(1), T(2), &ResultOverflowed));
+ EXPECT_EQ(T(3), SaturatingAdd(T(1), T(2), ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(Max, T(1)));
- EXPECT_EQ(Max, SaturatingAdd(Max, T(1), &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(Max, T(1), ResultOverflowed));
EXPECT_TRUE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 1)));
- EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 1), &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 1), ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(T(1), Max));
- EXPECT_EQ(Max, SaturatingAdd(T(1), Max, &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(T(1), Max, ResultOverflowed));
EXPECT_TRUE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(Max, Max));
- EXPECT_EQ(Max, SaturatingAdd(Max, Max, &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(Max, Max, ResultOverflowed));
EXPECT_TRUE(ResultOverflowed);
EXPECT_EQ(T(6), SaturatingAdd(T(1), T(2), T(3)));
- EXPECT_EQ(T(6), SaturatingAdd(T(1), T(2), T(3), &ResultOverflowed));
+ EXPECT_EQ(T(6), SaturatingAdd(T(1), T(2), T(3), ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(T(10), SaturatingAdd(T(1), T(2), T(3), T(4)));
- EXPECT_EQ(T(10), SaturatingAdd(T(1), T(2), T(3), T(4), &ResultOverflowed));
+ EXPECT_EQ(T(10), SaturatingAdd(T(1), T(2), T(3), T(4), ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(0)));
- EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(0), &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(0), ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(T(0), T(0), Max));
- EXPECT_EQ(Max, SaturatingAdd(T(0), T(0), Max, &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(T(0), T(0), Max, ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(1)));
- EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(1), &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(Max, T(0), T(1), ResultOverflowed));
EXPECT_TRUE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(T(0), T(1), Max));
- EXPECT_EQ(Max, SaturatingAdd(T(0), T(1), Max, &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(T(0), T(1), Max, ResultOverflowed));
EXPECT_TRUE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 2), T(1)));
- EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 2), T(1), &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 2), T(1), ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(T(1), T(1), T(Max - 2)));
- EXPECT_EQ(Max, SaturatingAdd(T(1), T(1), T(Max - 2), &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(T(1), T(1), T(Max - 2), ResultOverflowed));
EXPECT_FALSE(ResultOverflowed);
EXPECT_EQ(Max, SaturatingAdd(Max, Max, Max));
- EXPECT_EQ(Max, SaturatingAdd(Max, Max, Max, &ResultOverflowed));
+ EXPECT_EQ(Max, SaturatingAdd(Max, Max, Max, ResultOverflowed));
EXPECT_TRUE(ResultOverflowed);
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/190479
More information about the llvm-commits
mailing list