[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