Skip to content
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

[ADT] Add implementations for avgFloor and avgCeil to APInt #84431

Merged
merged 1 commit into from
Mar 14, 2024

Conversation

Atousa
Copy link
Contributor

@Atousa Atousa commented Mar 8, 2024

Supports both signed and unsigned expansions.
SelectionDAG now calls the APInt implementation of these functions.

Fixes #84211.

@Atousa Atousa marked this pull request as ready for review March 8, 2024 05:33
@llvmbot
Copy link
Collaborator

llvmbot commented Mar 8, 2024

@llvm/pr-subscribers-llvm-selectiondag
@llvm/pr-subscribers-llvm-adt

@llvm/pr-subscribers-llvm-support

Author: Atousa Duprat (Atousa)

Changes

Supports both signed and unsigned expansions.
SelectionDAG now calls the APInt implementation of these functions.

Issue #84211.


Full diff: https://github.com/llvm/llvm-project/pull/84431.diff

4 Files Affected:

  • (modified) llvm/include/llvm/ADT/APInt.h (+12)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp (+8-24)
  • (modified) llvm/lib/Support/APInt.cpp (+36)
  • (modified) llvm/unittests/ADT/APIntTest.cpp (+50)
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},

@@ -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) {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove extra newlines

@RKSimon RKSimon requested a review from davemgreen March 8, 2024 09:53
@jayfoad
Copy link
Contributor

jayfoad commented Mar 8, 2024

[ADT] Add implementations for avgFloor and ceilFloor to APInt

Typo "ceilFloor"

@@ -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
Copy link
Contributor

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 ///

Copy link
Contributor

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.

@Atousa Atousa changed the title [ADT] Add implementations for avgFloor and ceilFloor to APInt [ADT] Add implementations for avgFloor and avgCeil to APInt Mar 8, 2024
/// 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
Copy link
Contributor

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.

@jayfoad
Copy link
Contributor

jayfoad commented Mar 8, 2024

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.

@RKSimon
Copy link
Collaborator

RKSimon commented Mar 8, 2024

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 :)

@Atousa
Copy link
Contributor Author

Atousa commented Mar 8, 2024

@RKSimon @jayfoad Any further comments?

@Atousa
Copy link
Contributor Author

Atousa commented Mar 8, 2024

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?

llvm/lib/Support/APInt.cpp Outdated Show resolved Hide resolved
@RKSimon
Copy link
Collaborator

RKSimon commented Mar 9, 2024

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

@RKSimon
Copy link
Collaborator

RKSimon commented Mar 9, 2024

I've also raised #84640 for equivalent KnownBits handling

@Atousa
Copy link
Contributor Author

Atousa commented Mar 11, 2024

humble ping @nikic @davemgreen @goldsteinn . Thanks!

@@ -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))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/2 -> / 2

@@ -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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Return floor((C1 + C2)/2))
// Return floor((C1 + C2)/2)

Similarly for the other comments below

}

APInt APIntOps::avgCeilU(const APInt &C1, const APInt &C2) {
// Return ceil((C1 + C2)/2))
Copy link
Collaborator

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()

@@ -14,6 +14,7 @@
#include "llvm/Support/Alignment.h"
#include "gtest/gtest.h"
#include <array>
#include <limits.h>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#include <climits>

Copy link
Collaborator

@RKSimon RKSimon left a 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);
Copy link
Collaborator

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope :)


APInt APIntOps::avgFloorS(const APInt &C1, const APInt &C2) {
// Return floor((C1 + C2)/2)
assert(C1.getBitWidth() == C2.getBitWidth());
Copy link
Collaborator

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");

Copy link
Contributor

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.
@Atousa
Copy link
Contributor Author

Atousa commented Mar 14, 2024

@jayfoad @RKSimon @kuhar @topperc @arsenm Thanks all for reviewing the PR!

Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - cheers

@RKSimon RKSimon merged commit aff0570 into llvm:main Mar 14, 2024
4 checks passed
@antmox
Copy link
Contributor

antmox commented Mar 15, 2024

Hi, looks like this commit broke clang-arm64-windows-msvc-2stage bot:
https://lab.llvm.org/buildbot/#/builders/120/builds/6362

C:\Users\Tcwg\llvm-worker\clang-arm64-windows-msvc-2stage\llvm\llvm\unittests\ADT\APIntTest.cpp(2921,9): error: call to constructor of 'APInt' is ambiguous
 2921 |   APInt ApUMax(32, UINT_MAX, false);
      |         ^      ~~~~~~~~~~~~~~~~~~~
C:\Users\Tcwg\llvm-worker\clang-arm64-windows-msvc-2stage\llvm\llvm\include\llvm/ADT/APInt.h(109,3): note: candidate constructor
  109 |   APInt(unsigned numBits, uint64_t val, bool isSigned = false)
      |   ^
C:\Users\Tcwg\llvm-worker\clang-arm64-windows-msvc-2stage\llvm\llvm\include\llvm/ADT/APInt.h(135,3): note: candidate constructor
  135 |   APInt(unsigned numBits, unsigned numWords, const uint64_t bigVal[]);
      |   ^
1 error generated.

Could you please look at this ?

@RKSimon
Copy link
Collaborator

RKSimon commented Mar 15, 2024

I've pushed a fix here 41bdcaa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ADT] Add APIntOps::avgfloors/avgflooru/avgceils/avgceilu
8 participants