[llvm] 727ee7e - [APInt] Introduce carry-less multiply primitives (#168527)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 18 08:54:16 PST 2025
Author: Ramkumar Ramachandra
Date: 2025-11-18T16:54:12Z
New Revision: 727ee7e2f169ec60797004dfb9b29ef7ea7cc47a
URL: https://github.com/llvm/llvm-project/commit/727ee7e2f169ec60797004dfb9b29ef7ea7cc47a
DIFF: https://github.com/llvm/llvm-project/commit/727ee7e2f169ec60797004dfb9b29ef7ea7cc47a.diff
LOG: [APInt] Introduce carry-less multiply primitives (#168527)
In line with a std proposal to introduce std::clmul, and in preparation
to introduce a clmul intrinsic, implement carry-less multiply primitives
for APIntOps, clmul[rh].
Ref: https://isocpp.org/files/papers/P3642R3.html
Added:
Modified:
llvm/include/llvm/ADT/APInt.h
llvm/lib/Support/APInt.cpp
llvm/unittests/ADT/APIntTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index fdb3b84b73a1f..7e73cc1957c05 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -2440,6 +2440,27 @@ LLVM_ABI APInt fshl(const APInt &Hi, const APInt &Lo, const APInt &Shift);
/// (4) fshr(i8 255, i8 0, i8 9) = fshr(i8 255, i8 0, i8 1) // 9 % 8
LLVM_ABI APInt fshr(const APInt &Hi, const APInt &Lo, const APInt &Shift);
+/// Perform a carry-less multiply, also known as XOR multiplication, and return
+/// low-bits. All arguments and result have the same bitwidth.
+///
+/// Examples:
+/// (1) clmul(i4 1, i4 2) = 2
+/// (2) clmul(i4 5, i4 6) = 14
+/// (3) clmul(i4 -4, i4 2) = -8
+/// (4) clmul(i4 -4, i4 -5) = 4
+LLVM_ABI APInt clmul(const APInt &LHS, const APInt &RHS);
+
+/// Perform a reversed carry-less multiply.
+///
+/// clmulr(a, b) = bitreverse(clmul(bitreverse(a), bitreverse(b)))
+LLVM_ABI APInt clmulr(const APInt &LHS, const APInt &RHS);
+
+/// Perform a carry-less multiply, and return high-bits. All arguments and
+/// result have the same bitwidth.
+///
+/// clmulh(a, b) = clmulr(a, b) >> 1
+LLVM_ABI APInt clmulh(const APInt &LHS, const APInt &RHS);
+
} // namespace APIntOps
// See friend declaration above. This additional declaration is required in
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index f6fd5f9ddd633..673cd867f0e45 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -15,10 +15,10 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/bit.h"
-#include "llvm/Config/llvm-config.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@@ -3187,3 +3187,23 @@ APInt llvm::APIntOps::fshr(const APInt &Hi, const APInt &Lo,
return Lo;
return Hi.shl(Hi.getBitWidth() - ShiftAmt) | Lo.lshr(ShiftAmt);
}
+
+APInt llvm::APIntOps::clmul(const APInt &LHS, const APInt &RHS) {
+ assert(LHS.getBitWidth() == RHS.getBitWidth());
+ unsigned BW = LHS.getBitWidth();
+ APInt Result(BW, 0);
+ for (unsigned I : seq<unsigned>(BW))
+ if (RHS[I])
+ Result ^= LHS.shl(I);
+ return Result;
+}
+
+APInt llvm::APIntOps::clmulr(const APInt &LHS, const APInt &RHS) {
+ assert(LHS.getBitWidth() == RHS.getBitWidth());
+ return clmul(LHS.reverseBits(), RHS.reverseBits()).reverseBits();
+}
+
+APInt llvm::APIntOps::clmulh(const APInt &LHS, const APInt &RHS) {
+ assert(LHS.getBitWidth() == RHS.getBitWidth());
+ return clmulr(LHS, RHS).lshr(1);
+}
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index ca9f9f17ee112..4cb537da72e87 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -3823,4 +3823,87 @@ TEST(APIntTest, Fshr) {
-8193);
}
+TEST(APIntTest, clmul) {
+ EXPECT_EQ(APIntOps::clmul(APInt(4, 1), APInt(4, 2)).getZExtValue(), 2U);
+ EXPECT_EQ(APIntOps::clmul(APInt(4, 5), APInt(4, 6)).getZExtValue(), 14U);
+ EXPECT_EQ(APIntOps::clmul(APInt(4, -4, /*isSigned*/ true),
+ APInt(4, 2, /*isSigned*/ false))
+ .getSExtValue(),
+ -8);
+ EXPECT_EQ(APIntOps::clmul(APInt(4, -4, /*isSigned*/ true),
+ APInt(4, -5, /*isSigned*/ true))
+ .getSExtValue(),
+ 4);
+ EXPECT_EQ(APIntOps::clmul(APInt(8, 0), APInt(8, 255)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmul(APInt(8, 15), APInt(8, 15)).getZExtValue(), 85U);
+ EXPECT_EQ(APIntOps::clmul(APInt(8, 1), APInt(8, 2)).getZExtValue(), 2U);
+ EXPECT_EQ(APIntOps::clmul(APInt(64, 0, /*isSigned*/ true),
+ APInt(64, 9223372036854775807, /*isSigned*/ true))
+ .getSExtValue(),
+ 0);
+ EXPECT_EQ(APIntOps::clmul(APInt(64, 1, /*isSigned*/ true),
+ APInt(64, 2, /*isSigned*/ true))
+ .getSExtValue(),
+ 2);
+ EXPECT_EQ(APIntOps::clmul(APInt(16, -2, /*isSigned*/ true),
+ APInt(16, -1, /*isSigned*/ true))
+ .getSExtValue(),
+ -21846);
+}
+
+TEST(APIntTest, clmulr) {
+ EXPECT_EQ(APIntOps::clmulr(APInt(4, 1), APInt(4, 2)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulr(APInt(4, 5), APInt(4, 6)).getZExtValue(), 3U);
+ EXPECT_EQ(APIntOps::clmulr(APInt(4, -4, /*isSigned*/ true),
+ APInt(4, 2, /*isSigned*/ false))
+ .getSExtValue(),
+ 3);
+ EXPECT_EQ(APIntOps::clmulr(APInt(4, -4, /*isSigned*/ true),
+ APInt(4, -5, /*isSigned*/ true))
+ .getSExtValue(),
+ -2);
+ EXPECT_EQ(APIntOps::clmulr(APInt(8, 0), APInt(8, 255)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulr(APInt(8, 15), APInt(8, 15)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulr(APInt(8, 1), APInt(8, 2)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulr(APInt(64, 0, /*isSigned*/ true),
+ APInt(64, 9223372036854775807, /*isSigned*/ true))
+ .getSExtValue(),
+ 0);
+ EXPECT_EQ(APIntOps::clmulr(APInt(64, 1, /*isSigned*/ true),
+ APInt(64, 2, /*isSigned*/ true))
+ .getSExtValue(),
+ 0);
+ EXPECT_EQ(APIntOps::clmulr(APInt(16, -2, /*isSigned*/ true),
+ APInt(16, -1, /*isSigned*/ true))
+ .getSExtValue(),
+ -21845);
+}
+
+TEST(APIntTest, clmulh) {
+ EXPECT_EQ(APIntOps::clmulh(APInt(4, 1), APInt(4, 2)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulh(APInt(4, 5), APInt(4, 6)).getZExtValue(), 1U);
+ EXPECT_EQ(APIntOps::clmulh(APInt(4, -4, /*isSigned*/ true),
+ APInt(4, 2, /*isSigned*/ false))
+ .getSExtValue(),
+ 1);
+ EXPECT_EQ(APIntOps::clmulh(APInt(4, -4, /*isSigned*/ true),
+ APInt(4, -5, /*isSigned*/ true))
+ .getSExtValue(),
+ 7);
+ EXPECT_EQ(APIntOps::clmulh(APInt(8, 0), APInt(8, 255)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulh(APInt(8, 15), APInt(8, 15)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulh(APInt(8, 1), APInt(8, 2)).getZExtValue(), 0U);
+ EXPECT_EQ(APIntOps::clmulh(APInt(64, 0, /*isSigned*/ true),
+ APInt(64, 9223372036854775807, /*isSigned*/ true))
+ .getSExtValue(),
+ 0);
+ EXPECT_EQ(APIntOps::clmulh(APInt(64, 1, /*isSigned*/ true),
+ APInt(64, 2, /*isSigned*/ true))
+ .getSExtValue(),
+ 0);
+ EXPECT_EQ(APIntOps::clmulh(APInt(16, -2, /*isSigned*/ true),
+ APInt(16, -1, /*isSigned*/ true))
+ .getSExtValue(),
+ 21845);
+}
} // end anonymous namespace
More information about the llvm-commits
mailing list