-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[ADT] Add implementations for avgFloor and avgCeil to APInt #84431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-llvm-selectiondag @llvm/pr-subscribers-llvm-support Author: Atousa Duprat (Atousa) ChangesSupports both signed and unsigned expansions. Issue #84211. Full diff: https://github.com/llvm/llvm-project/pull/84431.diff 4 Files Affected:
diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 1fc3c7b2236a17..72f88fd956f2f3 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -2193,6 +2193,18 @@ inline const APInt absdiff(const APInt &A, const APInt &B) {
return A.uge(B) ? (A - B) : (B - A);
}
+///.Compute the signed floor of the average of C1 and C2
+APInt avgFloorS(const APInt &C1, const APInt &C2);
+
+///.Compute the floor of the average of C1 and C2
+APInt avgFloorU(const APInt &C1, const APInt &C2);
+
+///.Compute the ceil of the average of C1 and C2
+APInt avgCeilS(const APInt &C1, const APInt &C2);
+
+///.Compute the ceil of the average of C1 and C2
+APInt avgCeilU(const APInt &C1, const APInt &C2);
+
/// Compute GCD of two unsigned APInt values.
///
/// This function returns the greatest common divisor of the two APInt values
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index f7ace79e8c51d4..a844a00be16291 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -6021,30 +6021,14 @@ static std::optional<APInt> FoldValue(unsigned Opcode, const APInt &C1,
APInt C2Ext = C2.zext(FullWidth);
return (C1Ext * C2Ext).extractBits(C1.getBitWidth(), C1.getBitWidth());
}
- case ISD::AVGFLOORS: {
- unsigned FullWidth = C1.getBitWidth() + 1;
- APInt C1Ext = C1.sext(FullWidth);
- APInt C2Ext = C2.sext(FullWidth);
- return (C1Ext + C2Ext).extractBits(C1.getBitWidth(), 1);
- }
- case ISD::AVGFLOORU: {
- unsigned FullWidth = C1.getBitWidth() + 1;
- APInt C1Ext = C1.zext(FullWidth);
- APInt C2Ext = C2.zext(FullWidth);
- return (C1Ext + C2Ext).extractBits(C1.getBitWidth(), 1);
- }
- case ISD::AVGCEILS: {
- unsigned FullWidth = C1.getBitWidth() + 1;
- APInt C1Ext = C1.sext(FullWidth);
- APInt C2Ext = C2.sext(FullWidth);
- return (C1Ext + C2Ext + 1).extractBits(C1.getBitWidth(), 1);
- }
- case ISD::AVGCEILU: {
- unsigned FullWidth = C1.getBitWidth() + 1;
- APInt C1Ext = C1.zext(FullWidth);
- APInt C2Ext = C2.zext(FullWidth);
- return (C1Ext + C2Ext + 1).extractBits(C1.getBitWidth(), 1);
- }
+ case ISD::AVGFLOORS:
+ return APIntOps::avgFloorS(C1, C2);
+ case ISD::AVGFLOORU:
+ return APIntOps::avgFloorU(C1, C2);
+ case ISD::AVGCEILS:
+ return APIntOps::avgCeilS(C1, C2);
+ case ISD::AVGCEILU:
+ return APIntOps::avgCeilU(C1, C2);
case ISD::ABDS:
return APIntOps::smax(C1, C2) - APIntOps::smin(C1, C2);
case ISD::ABDU:
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index e686b976523302..4c2188dbbbfb7f 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -3094,3 +3094,39 @@ void llvm::LoadIntFromMemory(APInt &IntVal, const uint8_t *Src,
memcpy(Dst + sizeof(uint64_t) - LoadBytes, Src, LoadBytes);
}
}
+
+APInt APIntOps::avgFloorS(const APInt &C1, const APInt &C2) {
+
+ // Return floor((C1 + C2) /2))
+ unsigned FullWidth = C1.getBitWidth() + 1;
+ APInt C1Ext = C1.sext(FullWidth);
+ APInt C2Ext = C2.sext(FullWidth);
+ return (C1Ext + C2Ext).extractBits(C1.getBitWidth(), 1);
+}
+
+APInt APIntOps::avgFloorU(const APInt &C1, const APInt &C2) {
+
+ // Return floor((C1 + C2) /2))
+ unsigned FullWidth = C1.getBitWidth() + 1;
+ APInt C1Ext = C1.zext(FullWidth);
+ APInt C2Ext = C2.zext(FullWidth);
+ return (C1Ext + C2Ext).extractBits(C1.getBitWidth(), 1);
+}
+
+APInt APIntOps::avgCeilS(const APInt &C1, const APInt &C2) {
+
+ // Return ceil((C1 + C2) /2))
+ unsigned FullWidth = C1.getBitWidth() + 1;
+ APInt C1Ext = C1.sext(FullWidth);
+ APInt C2Ext = C2.sext(FullWidth);
+ return (C1Ext + C2Ext + 1).extractBits(C1.getBitWidth(), 1);
+}
+
+APInt APIntOps::avgCeilU(const APInt &C1, const APInt &C2) {
+
+ // Return ceil((C1 + C2) /2))
+ unsigned FullWidth = C1.getBitWidth() + 1;
+ APInt C1Ext = C1.zext(FullWidth);
+ APInt C2Ext = C2.zext(FullWidth);
+ return (C1Ext + C2Ext + 1).extractBits(C1.getBitWidth(), 1);
+}
\ No newline at end of file
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index 24324822356bf6..48beccb1f5167a 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -2877,6 +2877,56 @@ TEST(APIntTest, RoundingSDiv) {
}
}
+TEST(APIntTest, Average) {
+ APInt A2(32, 2);
+ APInt A100(32, 100);
+ APInt A101(32, 101);
+ APInt A200(32, 200, false);
+ APInt avg = APIntOps::avgFloorU(A100, A200);
+
+ EXPECT_EQ(APInt(32, 150), APIntOps::avgFloorU(A100, A200));
+ EXPECT_EQ(APIntOps::RoundingUDiv(A100 + A200, A2, APInt::Rounding::DOWN),
+ APIntOps::avgFloorU(A100, A200));
+ EXPECT_EQ(APIntOps::RoundingUDiv(A100 + A200, A2, APInt::Rounding::UP),
+ APIntOps::avgCeilU(A100, A200));
+ EXPECT_EQ(APIntOps::RoundingUDiv(A100 + A101, A2, APInt::Rounding::DOWN),
+ APIntOps::avgFloorU(A100, A101));
+ EXPECT_EQ(APIntOps::RoundingUDiv(A100 + A101, A2, APInt::Rounding::UP),
+ APIntOps::avgCeilU(A100, A101));
+
+ APInt Ap100(32, +100);
+ APInt Ap101(32, +101);
+ APInt Ap200(32, +200);
+ APInt Am100(32, -100);
+ APInt Am101(32, -101);
+ APInt Am200(32, -200);
+ EXPECT_EQ(APInt(32, +150), APIntOps::avgFloorS(Ap100, Ap200));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap200, A2, APInt::Rounding::DOWN),
+ APIntOps::avgFloorS(Ap100, Ap200));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap200, A2, APInt::Rounding::UP),
+ APIntOps::avgCeilS(Ap100, Ap200));
+
+ EXPECT_EQ(APInt(32, -150), APIntOps::avgFloorS(Am100, Am200));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am200, A2, APInt::Rounding::DOWN),
+ APIntOps::avgFloorS(Am100, Am200));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am200, A2, APInt::Rounding::UP),
+ APIntOps::avgCeilS(Am100, Am200));
+
+ EXPECT_EQ(APInt(32, +100), APIntOps::avgFloorS(Ap100, Ap101));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap101, A2, APInt::Rounding::DOWN),
+ APIntOps::avgFloorS(Ap100, Ap101));
+ EXPECT_EQ(APInt(32, +101), APIntOps::avgCeilS(Ap100, Ap101));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Ap100 + Ap101, A2, APInt::Rounding::UP),
+ APIntOps::avgCeilS(Ap100, Ap101));
+
+ EXPECT_EQ(APInt(32, -101), APIntOps::avgFloorS(Am100, Am101));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am101, A2, APInt::Rounding::DOWN),
+ APIntOps::avgFloorS(Am100, Am101));
+ EXPECT_EQ(APInt(32, -100), APIntOps::avgCeilS(Am100, Am101));
+ EXPECT_EQ(APIntOps::RoundingSDiv(Am100 + Am101, A2, APInt::Rounding::UP),
+ APIntOps::avgCeilS(Am100, Am101));
+}
+
TEST(APIntTest, umul_ov) {
const std::pair<uint64_t, uint64_t> Overflows[] = {
{0x8000000000000000, 2},
|
llvm/lib/Support/APInt.cpp
Outdated
@@ -3094,3 +3094,39 @@ void llvm::LoadIntFromMemory(APInt &IntVal, const uint8_t *Src, | |||
memcpy(Dst + sizeof(uint64_t) - LoadBytes, Src, LoadBytes); | |||
} | |||
} | |||
|
|||
APInt APIntOps::avgFloorS(const APInt &C1, const APInt &C2) { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove extra newlines
Typo "ceilFloor" |
llvm/include/llvm/ADT/APInt.h
Outdated
@@ -2193,6 +2193,18 @@ inline const APInt absdiff(const APInt &A, const APInt &B) { | |||
return A.uge(B) ? (A - B) : (B - A); | |||
} | |||
|
|||
///.Compute the signed floor of the average of C1 and C2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space instead of dot after ///
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the average that is signed or unsigned, not the floor. So the comment should be "Compute the floor of the signed average of C1 and C2". Similarly for the others.
llvm/include/llvm/ADT/APInt.h
Outdated
/// Compute the floor of the signed average of C1 and C2 | ||
APInt avgFloorS(const APInt &C1, const APInt &C2); | ||
|
||
/// Compute the floor of the average of C1 and C2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should say "unsigned average" here for consistency. Same for avgCeilU below.
Incidentally Hacker's Delight has a section "Average of Two Integers" which has some neat tricks for implementing these operations without extending to N+1-bit integers. |
Thanks, we use min/max/sat/cmpsel instructions on TargetLowering::expandABD - using those as fallbacks might be a nice followup good-first-issue for someone :) |
Can I open a new discussion regarding a better implementation of AvgFloor and AvgCeil in TargetLowering::expandABD? |
I've raised a ticket here #84639 |
I've also raised #84640 for equivalent KnownBits handling |
humble ping @nikic @davemgreen @goldsteinn . Thanks! |
llvm/lib/Support/APInt.cpp
Outdated
@@ -3094,3 +3094,35 @@ void llvm::LoadIntFromMemory(APInt &IntVal, const uint8_t *Src, | |||
memcpy(Dst + sizeof(uint64_t) - LoadBytes, Src, LoadBytes); | |||
} | |||
} | |||
|
|||
APInt APIntOps::avgFloorS(const APInt &C1, const APInt &C2) { | |||
// Return floor((C1 + C2) /2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/2
-> / 2
llvm/lib/Support/APInt.cpp
Outdated
@@ -3094,3 +3094,35 @@ void llvm::LoadIntFromMemory(APInt &IntVal, const uint8_t *Src, | |||
memcpy(Dst + sizeof(uint64_t) - LoadBytes, Src, LoadBytes); | |||
} | |||
} | |||
|
|||
APInt APIntOps::avgFloorS(const APInt &C1, const APInt &C2) { | |||
// Return floor((C1 + C2)/2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Return floor((C1 + C2)/2)) | |
// Return floor((C1 + C2)/2) |
Similarly for the other comments below
llvm/lib/Support/APInt.cpp
Outdated
} | ||
|
||
APInt APIntOps::avgCeilU(const APInt &C1, const APInt &C2) { | ||
// Return ceil((C1 + C2)/2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add assertions to all 4 methods that C1.getBitWidth() == C2.getBitWidth()
llvm/unittests/ADT/APIntTest.cpp
Outdated
@@ -14,6 +14,7 @@ | |||
#include "llvm/Support/Alignment.h" | |||
#include "gtest/gtest.h" | |||
#include <array> | |||
#include <limits.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include <climits>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of minors and a naming question - but other than that this almost looks ready
APInt avgCeilS(const APInt &C1, const APInt &C2); | ||
|
||
/// Compute the ceil of the unsigned average of C1 and C2 | ||
APInt avgCeilU(const APInt &C1, const APInt &C2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does anyone have any preference for camelcase vs lowercase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope :)
llvm/lib/Support/APInt.cpp
Outdated
|
||
APInt APIntOps::avgFloorS(const APInt &C1, const APInt &C2) { | ||
// Return floor((C1 + C2)/2) | ||
assert(C1.getBitWidth() == C2.getBitWidth()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(style) Assertions must have messages (same below):
assert(C1.getBitWidth() == C2.getBitWidth() && "Unequal bitwidths");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that's what the coding standards say, but it seems a bit silly when it encourages people to write a message that just says exactly the same as the condition, but in English.
Supports both signed and unsigned expansions. SelectionDAG now calls the APInt implementation of these functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - cheers
Hi, looks like this commit broke clang-arm64-windows-msvc-2stage bot:
Could you please look at this ? |
I've pushed a fix here 41bdcaa |
Supports both signed and unsigned expansions.
SelectionDAG now calls the APInt implementation of these functions.
Fixes #84211.