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

Sean Clarke via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 25 07:06:05 PDT 2026


================
@@ -1549,7 +1551,128 @@ bool LoopIdiomRecognize::avoidLIRForMultiBlockLoop(bool IsMemset,
   return false;
 }
 
-bool LoopIdiomRecognize::optimizeCRCLoop(const PolynomialInfo &Info) {
+bool LoopIdiomRecognize::optimizeCRCLoopToClmul(const PolynomialInfo &Info) {
+  Type *CRCTy = Info.LHS->getType();
+  LLVMContext &Ctx = CRCTy->getContext();
+  unsigned CRCBW = CRCTy->getIntegerBitWidth();
+  // The TripCount determines how many bits of data are processed, regardless of
+  // whether the actual data bit width matches (if auxiliary data is even used
+  // at all).
+  unsigned EffectiveDataBW = Info.TripCount;
+  // The width used for clmul operations should be a power of 2, and should be
+  // at least CRCBW + DataBW.
+  unsigned ClmulBW = 2 * std::max(CRCBW, EffectiveDataBW);
+  Type *ClmulTy = IntegerType::get(Ctx, ClmulBW);
+
+  // For big-endian CRC loops where the auxiliary data is XORed with the CRC
+  // inside the loop, the bits won't be aligned properly if the bit widths don't
+  // match, and thus the CRC computation is incorrect, but HashRecognize will
+  // still detect the loop. Since this optimization always produces a correct
+  // CRC computation, bail in this edge case.
+  if (Info.ByteOrderSwapped && Info.LHSAux &&
+      (EffectiveDataBW != CRCBW ||
+       Info.LHSAux->getType()->getIntegerBitWidth() != CRCBW))
+    return false;
+
+  // This optimization should not be applied if there is no fast clmul operation
+  // for the required width on the target.
+  // TODO: If EffectiveDataBW > CRCBW, then the data could probably be split
+  // into multiple chunks and processed in a loop.
+  if (!TTI->haveFastClmul(ClmulTy))
+    return false;
+
+  // First, generate the constants required for GF(2) Barrett reduction.
+  CRCBarrettConstants Constants = HashRecognize::genBarrettConstants(
+      Info.RHS, EffectiveDataBW, Info.ByteOrderSwapped);
+  Value *Mu = ConstantInt::get(Ctx, Constants.Mu.zext(ClmulBW));
+  Value *FullGenPoly =
+      ConstantInt::get(Ctx, Constants.FullGenPoly.zext(ClmulBW));
+
+  IRBuilder<> Builder(CurLoop->getLoopPreheader()->getTerminator());
----------------
xarkenz wrote:

I used `getLoopPreheader()->getTerminator()` so that all the new instructions would be placed outside the loop, and the loop would be removed with dead code elimination. Are you saying it would be better to put the new instructions inside the loop? The existing lookup table optimization initializes its builder at `getHeader()->getFirstNonPHIIt()`, which is inside the loop, so I'm not sure `getHeader()->getTerminator()` would be a useful starting point, but I could be wrong.

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


More information about the llvm-commits mailing list