[llvm] [LoopIdiomRecognize] Enable clmul optimization for CRC loops (PR #203405)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 13 03:25:50 PDT 2026


================
@@ -1665,24 +1649,105 @@ bool LoopIdiomRecognize::optimizeCRCLoop(const PolynomialInfo &Info) {
     Indexer = Info.ByteOrderSwapped ? HiIdx(Builder, Indexer, "indexer.hi")
                                     : LoByte(Builder, Indexer, "indexer.lo");
 
-    // Always index into a GEP using the index type.
-    Indexer = Builder.CreateZExt(
-        Indexer, SE->getDataLayout().getIndexType(GV->getType()),
-        "indexer.ext");
-
-    // CRCTableLd = CRCTable[(iv'th byte of data) ^ (top|bottom) byte of CRC].
-    Value *CRCTableGEP =
-        Builder.CreateInBoundsGEP(CRCTy, GV, Indexer, "tbl.ptradd");
-    Value *CRCTableLd = Builder.CreateLoad(CRCTy, CRCTableGEP, "tbl.ld");
-
-    // CRCNext = (CRC (<<|>>) 8) ^ CRCTableLd, or simply CRCTableLd in case of
-    // CRC-8.
-    Value *CRCNext = CRCTableLd;
-    if (CRCBW > 8) {
-      Value *CRCShift = Info.ByteOrderSwapped
-                            ? Builder.CreateShl(CRC, 8, "crc.be.shift")
-                            : Builder.CreateLShr(CRC, 8, "crc.le.shift");
-      CRCNext = Builder.CreateXor(CRCShift, CRCTableLd, "crc.next");
+    // Compute the next value of the CRC register based on Indexer. This could
+    // either be a table load or an on-the-fly computation, depending on whether
+    // llvm.clmul is fast on the target.
+    Value *CRCNext;
+    Type *ClmulTy = IntegerType::get(CRCTy->getContext(), 2 * CRCBW);
+    if (TTI->haveFastClmul(ClmulTy)) {
+      // Use a Barrett-style reduction to calculate CRCNext with two clmuls.
+      // Little-endian:
+      //   q = ((iv'th byte of data) ^ (bottom byte of crc)) clmul (reciprocal)
+      //   entry = (bottom byte of q) clmul (genpoly)
+      //   crc = (crc >> 8) ^ (entry >> 7)
+      //   Equivalently, to save one shift instruction:
+      //   (genBarrettConstants gives us genpoly << 1)
+      //   entry = (bottom byte of q) clmul (genpoly << 1)
+      //   crc = (crc ^ entry) >> 8
+      // Big-Endian:
+      //   q = ((iv'th byte of data) ^ (top byte of crc)) clmul (reciprocal)
+      //   entry = (top byte of q) clmul (genpoly)
+      //   crc = (crc << 8) ^ (bottom CRCBW bits of entry)
+
+      // First, generate the two constants required for a Barrett-style
+      // reduction.
+      CRCBarrettConstants Constants =
+          HashRecognize::genBarrettConstants(Info.RHS, Info.ByteOrderSwapped);
+      Value *Reciprocal = ConstantInt::get(ClmulTy, Constants.Reciprocal);
+      Value *Generator = ConstantInt::get(ClmulTy, Constants.Generator);
+
+      // Approximate floor(Indexer / GenPoly) using Indexer * Reciprocal.
+      Indexer = Builder.CreateZExt(Indexer, ClmulTy, "indexer.ext");
+      Value *Quotient = Builder.CreateBinaryIntrinsic(Intrinsic::clmul, Indexer,
+                                                      Reciprocal, {}, "quot");
+      Quotient = Info.ByteOrderSwapped
+                     ? Builder.CreateLShr(Quotient, CRCBW, "quot.be.shift")
+                     : Builder.CreateAnd(Quotient, 0xFF, "quot.le.mask");
+
+      // floor(Indexer / GenPoly) * GenPoly should give us the equivalent of the
+      // Sarwate table entry.
+      Value *ComputedByte = Builder.CreateBinaryIntrinsic(
+          Intrinsic::clmul, Quotient, Generator, {}, "entry");
----------------
artagnon wrote:

Can you kindly check your testing environment, because this is incorrect: we should never be using the Generator, once we have the Reciprocal.

https://github.com/llvm/llvm-project/pull/203405


More information about the llvm-commits mailing list