[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