Skip to content

Commit 727ee7e

Browse files
authored
[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
1 parent c7d2ed4 commit 727ee7e

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

llvm/include/llvm/ADT/APInt.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,27 @@ LLVM_ABI APInt fshl(const APInt &Hi, const APInt &Lo, const APInt &Shift);
24402440
/// (4) fshr(i8 255, i8 0, i8 9) = fshr(i8 255, i8 0, i8 1) // 9 % 8
24412441
LLVM_ABI APInt fshr(const APInt &Hi, const APInt &Lo, const APInt &Shift);
24422442

2443+
/// Perform a carry-less multiply, also known as XOR multiplication, and return
2444+
/// low-bits. All arguments and result have the same bitwidth.
2445+
///
2446+
/// Examples:
2447+
/// (1) clmul(i4 1, i4 2) = 2
2448+
/// (2) clmul(i4 5, i4 6) = 14
2449+
/// (3) clmul(i4 -4, i4 2) = -8
2450+
/// (4) clmul(i4 -4, i4 -5) = 4
2451+
LLVM_ABI APInt clmul(const APInt &LHS, const APInt &RHS);
2452+
2453+
/// Perform a reversed carry-less multiply.
2454+
///
2455+
/// clmulr(a, b) = bitreverse(clmul(bitreverse(a), bitreverse(b)))
2456+
LLVM_ABI APInt clmulr(const APInt &LHS, const APInt &RHS);
2457+
2458+
/// Perform a carry-less multiply, and return high-bits. All arguments and
2459+
/// result have the same bitwidth.
2460+
///
2461+
/// clmulh(a, b) = clmulr(a, b) >> 1
2462+
LLVM_ABI APInt clmulh(const APInt &LHS, const APInt &RHS);
2463+
24432464
} // namespace APIntOps
24442465

24452466
// See friend declaration above. This additional declaration is required in

llvm/lib/Support/APInt.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
#include "llvm/ADT/ArrayRef.h"
1616
#include "llvm/ADT/FoldingSet.h"
1717
#include "llvm/ADT/Hashing.h"
18+
#include "llvm/ADT/Sequence.h"
1819
#include "llvm/ADT/SmallString.h"
1920
#include "llvm/ADT/StringRef.h"
2021
#include "llvm/ADT/bit.h"
21-
#include "llvm/Config/llvm-config.h"
2222
#include "llvm/Support/Alignment.h"
2323
#include "llvm/Support/Debug.h"
2424
#include "llvm/Support/ErrorHandling.h"
@@ -3187,3 +3187,23 @@ APInt llvm::APIntOps::fshr(const APInt &Hi, const APInt &Lo,
31873187
return Lo;
31883188
return Hi.shl(Hi.getBitWidth() - ShiftAmt) | Lo.lshr(ShiftAmt);
31893189
}
3190+
3191+
APInt llvm::APIntOps::clmul(const APInt &LHS, const APInt &RHS) {
3192+
assert(LHS.getBitWidth() == RHS.getBitWidth());
3193+
unsigned BW = LHS.getBitWidth();
3194+
APInt Result(BW, 0);
3195+
for (unsigned I : seq<unsigned>(BW))
3196+
if (RHS[I])
3197+
Result ^= LHS.shl(I);
3198+
return Result;
3199+
}
3200+
3201+
APInt llvm::APIntOps::clmulr(const APInt &LHS, const APInt &RHS) {
3202+
assert(LHS.getBitWidth() == RHS.getBitWidth());
3203+
return clmul(LHS.reverseBits(), RHS.reverseBits()).reverseBits();
3204+
}
3205+
3206+
APInt llvm::APIntOps::clmulh(const APInt &LHS, const APInt &RHS) {
3207+
assert(LHS.getBitWidth() == RHS.getBitWidth());
3208+
return clmulr(LHS, RHS).lshr(1);
3209+
}

llvm/unittests/ADT/APIntTest.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3823,4 +3823,87 @@ TEST(APIntTest, Fshr) {
38233823
-8193);
38243824
}
38253825

3826+
TEST(APIntTest, clmul) {
3827+
EXPECT_EQ(APIntOps::clmul(APInt(4, 1), APInt(4, 2)).getZExtValue(), 2U);
3828+
EXPECT_EQ(APIntOps::clmul(APInt(4, 5), APInt(4, 6)).getZExtValue(), 14U);
3829+
EXPECT_EQ(APIntOps::clmul(APInt(4, -4, /*isSigned*/ true),
3830+
APInt(4, 2, /*isSigned*/ false))
3831+
.getSExtValue(),
3832+
-8);
3833+
EXPECT_EQ(APIntOps::clmul(APInt(4, -4, /*isSigned*/ true),
3834+
APInt(4, -5, /*isSigned*/ true))
3835+
.getSExtValue(),
3836+
4);
3837+
EXPECT_EQ(APIntOps::clmul(APInt(8, 0), APInt(8, 255)).getZExtValue(), 0U);
3838+
EXPECT_EQ(APIntOps::clmul(APInt(8, 15), APInt(8, 15)).getZExtValue(), 85U);
3839+
EXPECT_EQ(APIntOps::clmul(APInt(8, 1), APInt(8, 2)).getZExtValue(), 2U);
3840+
EXPECT_EQ(APIntOps::clmul(APInt(64, 0, /*isSigned*/ true),
3841+
APInt(64, 9223372036854775807, /*isSigned*/ true))
3842+
.getSExtValue(),
3843+
0);
3844+
EXPECT_EQ(APIntOps::clmul(APInt(64, 1, /*isSigned*/ true),
3845+
APInt(64, 2, /*isSigned*/ true))
3846+
.getSExtValue(),
3847+
2);
3848+
EXPECT_EQ(APIntOps::clmul(APInt(16, -2, /*isSigned*/ true),
3849+
APInt(16, -1, /*isSigned*/ true))
3850+
.getSExtValue(),
3851+
-21846);
3852+
}
3853+
3854+
TEST(APIntTest, clmulr) {
3855+
EXPECT_EQ(APIntOps::clmulr(APInt(4, 1), APInt(4, 2)).getZExtValue(), 0U);
3856+
EXPECT_EQ(APIntOps::clmulr(APInt(4, 5), APInt(4, 6)).getZExtValue(), 3U);
3857+
EXPECT_EQ(APIntOps::clmulr(APInt(4, -4, /*isSigned*/ true),
3858+
APInt(4, 2, /*isSigned*/ false))
3859+
.getSExtValue(),
3860+
3);
3861+
EXPECT_EQ(APIntOps::clmulr(APInt(4, -4, /*isSigned*/ true),
3862+
APInt(4, -5, /*isSigned*/ true))
3863+
.getSExtValue(),
3864+
-2);
3865+
EXPECT_EQ(APIntOps::clmulr(APInt(8, 0), APInt(8, 255)).getZExtValue(), 0U);
3866+
EXPECT_EQ(APIntOps::clmulr(APInt(8, 15), APInt(8, 15)).getZExtValue(), 0U);
3867+
EXPECT_EQ(APIntOps::clmulr(APInt(8, 1), APInt(8, 2)).getZExtValue(), 0U);
3868+
EXPECT_EQ(APIntOps::clmulr(APInt(64, 0, /*isSigned*/ true),
3869+
APInt(64, 9223372036854775807, /*isSigned*/ true))
3870+
.getSExtValue(),
3871+
0);
3872+
EXPECT_EQ(APIntOps::clmulr(APInt(64, 1, /*isSigned*/ true),
3873+
APInt(64, 2, /*isSigned*/ true))
3874+
.getSExtValue(),
3875+
0);
3876+
EXPECT_EQ(APIntOps::clmulr(APInt(16, -2, /*isSigned*/ true),
3877+
APInt(16, -1, /*isSigned*/ true))
3878+
.getSExtValue(),
3879+
-21845);
3880+
}
3881+
3882+
TEST(APIntTest, clmulh) {
3883+
EXPECT_EQ(APIntOps::clmulh(APInt(4, 1), APInt(4, 2)).getZExtValue(), 0U);
3884+
EXPECT_EQ(APIntOps::clmulh(APInt(4, 5), APInt(4, 6)).getZExtValue(), 1U);
3885+
EXPECT_EQ(APIntOps::clmulh(APInt(4, -4, /*isSigned*/ true),
3886+
APInt(4, 2, /*isSigned*/ false))
3887+
.getSExtValue(),
3888+
1);
3889+
EXPECT_EQ(APIntOps::clmulh(APInt(4, -4, /*isSigned*/ true),
3890+
APInt(4, -5, /*isSigned*/ true))
3891+
.getSExtValue(),
3892+
7);
3893+
EXPECT_EQ(APIntOps::clmulh(APInt(8, 0), APInt(8, 255)).getZExtValue(), 0U);
3894+
EXPECT_EQ(APIntOps::clmulh(APInt(8, 15), APInt(8, 15)).getZExtValue(), 0U);
3895+
EXPECT_EQ(APIntOps::clmulh(APInt(8, 1), APInt(8, 2)).getZExtValue(), 0U);
3896+
EXPECT_EQ(APIntOps::clmulh(APInt(64, 0, /*isSigned*/ true),
3897+
APInt(64, 9223372036854775807, /*isSigned*/ true))
3898+
.getSExtValue(),
3899+
0);
3900+
EXPECT_EQ(APIntOps::clmulh(APInt(64, 1, /*isSigned*/ true),
3901+
APInt(64, 2, /*isSigned*/ true))
3902+
.getSExtValue(),
3903+
0);
3904+
EXPECT_EQ(APIntOps::clmulh(APInt(16, -2, /*isSigned*/ true),
3905+
APInt(16, -1, /*isSigned*/ true))
3906+
.getSExtValue(),
3907+
21845);
3908+
}
38263909
} // end anonymous namespace

0 commit comments

Comments
 (0)