[llvm] [LoopIdiom] Use HashRecognize to optimize CRC (PR #143208)

Piotr Fusik via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 9 00:56:38 PDT 2025


================
@@ -1473,6 +1494,146 @@ bool LoopIdiomRecognize::avoidLIRForMultiBlockLoop(bool IsMemset,
   return false;
 }
 
+bool LoopIdiomRecognize::optimizeCRCLoop(const PolynomialInfo &Info) {
+  // We currently only optimize CRC loops with TC a multiple of 8 with a table
+  // lookup: the table-lookup can be improved using target-specific
+  // instructions.
+  if (Info.TripCount % 8 != 0)
+    return false;
+
+  // First, create a new GlobalVariable corresponding to the
+  // Sarwate-lookup-table.
+  Type *CRCTy = Info.LHS->getType();
+  unsigned CRCBW = CRCTy->getIntegerBitWidth();
+  std::array<Constant *, 256> CRCConstants;
+  transform(Info.SarwateTable, CRCConstants.begin(),
+            [CRCTy](const APInt &E) { return ConstantInt::get(CRCTy, E); });
+  Constant *ConstArray =
+      ConstantArray::get(ArrayType::get(CRCTy, 256), CRCConstants);
+  Module &M = *CurLoop->getHeader()->getModule();
+  GlobalVariable *GV =
+      new GlobalVariable(M, ConstArray->getType(), true,
+                         GlobalValue::PrivateLinkage, ConstArray, ".crctable");
+
+  PHINode *IV = CurLoop->getCanonicalInductionVariable();
+  Type *IVTy = IV->getType();
+  SmallVector<PHINode *, 2> Cleanup;
+
+  // Next, mark all PHIs for removal except IV.
+  {
+    for (PHINode &PN : CurLoop->getHeader()->phis()) {
+      if (&PN == IV)
+        continue;
+      PN.replaceAllUsesWith(PoisonValue::get(PN.getType()));
+      Cleanup.push_back(&PN);
+    }
+  }
+
+  // Next, fix up the trip count.
+  {
+    unsigned NewBTC = (Info.TripCount / 8) - 1;
+    BasicBlock *LoopBlk = CurLoop->getLoopLatch();
+    BranchInst *BrInst = cast<BranchInst>(LoopBlk->getTerminator());
+    CmpPredicate ExitPred = BrInst->getSuccessor(0) == LoopBlk
+                                ? ICmpInst::Predicate::ICMP_NE
+                                : ICmpInst::Predicate::ICMP_EQ;
+    Instruction *ExitCond = CurLoop->getLatchCmpInst();
+    IRBuilder<> Builder(ExitCond);
+    Value *NewExitCond = Builder.CreateICmp(
+        ExitPred, IV, ConstantInt::get(IVTy, NewBTC), "exit.cond");
+    ExitCond->replaceAllUsesWith(NewExitCond);
+    deleteDeadInstruction(ExitCond);
+  }
+
+  // Finally, fill the loop with the Sarwate-table-lookup logic, and replace all
+  // uses of ComputedValue.
+  //
+  // Little-endian:
+  //   crc = (crc >> 8) ^ tbl[data[iv] ^ (crc & 0xFF)]
+  // Big-Endian:
+  //   crc = (crc << 8) ^ tbl[data[iv] ^ (crc >> (crc.bw - 8))]
+  {
+    // Compute the top 8 bits of Op.
+    auto GetLoByte = [](IRBuilderBase &Builder, Value *Op, const Twine &Name) {
+      Type *OpTy = Op->getType();
+      return Builder.CreateAnd(Op, ConstantInt::get(OpTy, 0XFF), Name);
+    };
+    auto GetHiByte = [](IRBuilderBase &Builder, Value *Op, const Twine &Name) {
+      Type *OpTy = Op->getType();
+      unsigned OpBW = OpTy->getIntegerBitWidth();
+      return Builder.CreateLShr(
+          Builder.CreateAnd(
+              Op, ConstantInt::get(OpTy, APInt::getHighBitsSet(OpBW, 8)), Name),
+          ConstantInt::get(OpTy, OpBW - 8), Name + ".hi.extract");
+    };
+
+    IRBuilder<> Builder(CurLoop->getHeader(),
+                        CurLoop->getHeader()->getFirstNonPHIIt());
+    // Create the CRC PHI, and initialize its incoming value to AllOnes from the
+    // entry block.
+    PHINode *CRCPhi = Builder.CreatePHI(CRCTy, 2, "crc");
+    Constant *AllOnes = ConstantInt::get(CRCTy, APInt::getAllOnes(CRCBW));
+    CRCPhi->addIncoming(AllOnes, CurLoop->getLoopPreheader());
+
+    // CRC is now an evolving variable, initialized to the PHI.
+    Value *CRC = CRCPhi;
+
+    // Data is either LHSAux, if one is evolving in the loop or a loop-invariant
+    // LHS.
+    if (Value *Data = Info.LHSAux ? Info.LHSAux : Info.LHS) {
+      // Compute Data[iv], when Data is an integer.
+      Value *IVLo = Builder.CreateZExtOrTrunc(
+          Builder.CreateShl(IV, ConstantInt::get(IVTy, 3), "iv.bits"),
+          Data->getType(), "iv.indexer");
+      Value *DataIndexer =
+          GetHiByte(Builder, Builder.CreateShl(Data, IVLo, "data.lo.indexer"),
+                    "data.hi.mask");
+      CRC = Builder.CreateXor(
+          CRC, Builder.CreateZExtOrTrunc(DataIndexer, CRCTy, "data.indexer"),
+          "xor.crc.data");
+    }
+
+    // CRCShift = CRC (<<|>>) 8. No shift in case of CRC-8.
+    Value *CRCShift = nullptr;
+    if (CRCBW > 8)
+      CRCShift = Info.ByteOrderSwapped
+                     ? Builder.CreateShl(CRC, 8, "crc.be.shift")
+                     : Builder.CreateLShr(CRC, 8, "crc.le.shift");
+
+    // CRCTableLd = CRCTable[(top|bottom) byte of CRC].
+    Value *CRCIndexer = Info.ByteOrderSwapped
+                            ? GetHiByte(Builder, CRC, "crc.hi.mask")
+                            : GetLoByte(Builder, CRC, "crc.lo.mask");
+    Value *CRCTableGEP = Builder.CreatePtrAdd(GV, CRCIndexer, "crc.tbl.indexer",
+                                              GEPNoWrapFlags::inBounds());
+    Value *CRCTableLd = Builder.CreateLoad(CRCTy, CRCTableGEP, "crc.tbl.index");
+
+    // CRC = CRCShift ^ CRCableLd. No XOR with shift in case of CRC-8.
----------------
pfusik wrote:

```suggestion
    // CRC = CRCShift ^ CRCTableLd. No XOR with shift in case of CRC-8.
```

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


More information about the llvm-commits mailing list