[llvm] [LoopIdiomRecognize] Enable clmul optimization for CRC loops (PR #203405)
Sean Clarke via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 2 07:50:14 PDT 2026
================
@@ -1549,7 +1554,156 @@ bool LoopIdiomRecognize::avoidLIRForMultiBlockLoop(bool IsMemset,
return false;
}
-bool LoopIdiomRecognize::optimizeCRCLoop(const PolynomialInfo &Info) {
+// The algorithm used in this optimization is a Polynomial (GF(2)) Barrett
+// Reduction based on Intel's "Fast CRC Computation for Generic Polynomials
+// Using PCLMULQDQ Instruction" white paper (December 2009).
+bool LoopIdiomRecognize::optimizeCRCLoopUsingClmul(const PolynomialInfo &Info) {
+ Type *CRCTy = Info.LHS->getType();
+ LLVMContext &Ctx = CRCTy->getContext();
+ unsigned CRCBW = CRCTy->getIntegerBitWidth();
+ // The loop's TripCount determines how many bits of the data are processed,
+ // regardless of whether the actual data bit width matches (if auxiliary data
+ // is even used at all).
+ unsigned TC = Info.TripCount;
+ // The first clmul uses 2*TC bits, and the second clmul uses CRCBW+TC bits.
+ // For simplicity, have both operate on the same bit width.
+ unsigned ClmulBW = std::max(2 * TC, CRCBW + TC);
+ auto *ClmulTy = IntegerType::get(Ctx, ClmulBW);
+
+ // This optimization should only be applied if clmul for the required width is
+ // a fast operation on the target.
+ // TODO: If clmul exists on the target but not for the required width, it
+ // might be possible to split into multiple iterations of this.
+ if (!TTI->haveFastClmul(ClmulTy))
+ return false;
+
+ // First, generate the constants required for GF(2) Barrett reduction.
+ auto [Mu, FullGenPoly] =
+ HashRecognize::genBarrettConstants(Info.RHS, TC, Info.IsBigEndian);
+ Value *MuConst = ConstantInt::get(Ctx, Mu.zext(ClmulBW));
+ Value *GenPolyConst = ConstantInt::get(Ctx, FullGenPoly.zext(ClmulBW));
+
+ IRBuilder<> Builder(CurLoop->getLoopPreheader()->getTerminator());
+
+ auto ShiftNetAmt = [&](Value *Op, unsigned LShrAmt, unsigned ShlAmt,
+ const Twine &Name) {
+ if (LShrAmt > ShlAmt)
+ return Builder.CreateLShr(Op, LShrAmt - ShlAmt, Name);
+ if (ShlAmt > LShrAmt)
+ return Builder.CreateShl(Op, ShlAmt - LShrAmt, Name);
+ return Op;
+ };
+
+ auto LoTCBits = [&](Value *Op, const Twine &Name) {
+ unsigned OpBW = Op->getType()->getIntegerBitWidth();
+ assert(OpBW >= TC && "Bit width should be at least TripCount");
+ auto *Mask = ConstantInt::get(Ctx, APInt::getLowBitsSet(OpBW, TC));
+ return Builder.CreateAnd(Op, Mask, Name);
+ };
+
+ auto MostSignificantTCBits = [&](Value *Op, unsigned BW, const Twine &Name) {
----------------
xarkenz wrote:
I went ahead and got rid of the lambda since that makes the logic a little clearer, I think.
https://github.com/llvm/llvm-project/pull/203405
More information about the llvm-commits
mailing list