[llvm] [LoopIdiom] Use costing to determine CRC strategy (PR #211040)

Piotr Fusik via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 23 07:58:09 PDT 2026


================
@@ -1589,43 +1603,110 @@ bool LoopIdiomRecognize::optimizeCRCLoop(const PolynomialInfo &Info) {
   if (TT.getArch() == Triple::hexagon)
     return false;
 
-  // The force-crc-clmul flag should cause the clmul optimization to run
-  // unconditionally.
-  if (ForceCRCClmul) {
+  // The Sarwate lookup table optimization requires a byte-multiple trip count,
+  // and should not be applied under any circumstances if optimizing for size.
+  bool TableStrategyPossible =
+      Info.TripCount % 8 == 0 && !ApplyCodeSizeHeuristics;
+
+  switch (CRCStrategy) {
+  default:
+    break;
+  case CRCStrategyKind::Table: {
+    if (TableStrategyPossible) {
+      optimizeCRCLoopUsingTableLookup(Info);
+      return true;
+    }
+    return false;
+  }
+  case CRCStrategyKind::Clmul:
     optimizeCRCLoopUsingClmul(Info);
     return true;
   }
 
-  // FIXME: Once intrinsic cost modeling is more reliable for clmul, that should
-  // be used to determine which optimization to use. Until then, only apply the
-  // clmul optimization when optimizing for size, since a lookup table is not
-  // viable in that case.
-  if (!ApplyCodeSizeHeuristics && Info.TripCount % 8 == 0) {
-    optimizeCRCLoopUsingTableLookup(Info);
-    return true;
+  // When using the auto strategy, bail if we are optimizing for size since
+  // there's usually not a clear size benefit.
+  // TODO: The clmul optimization is around the same size in many cases, so it
+  // could be worth it to take advantage of that fact, especially if it would be
+  // much faster than the original loop.
+  if (ApplyCodeSizeHeuristics)
+    return false;
+
+  LLVMContext &Ctx = Info.LHS->getContext();
+  Type *CRCTy = Info.LHS->getType();
+  unsigned CRCBW = CRCTy->getIntegerBitWidth();
+
+  // CRC computation is mostly serial, so latency works best for comparison.
+  TargetTransformInfo::TargetCostKind CostKind =
+      TargetTransformInfo::TCK_Latency;
+
+  LLVM_DEBUG(dbgs() << DEBUG_TYPE " CRC: BW=" << CRCBW
+                    << " TC=" << Info.TripCount);
+
+  InstructionCost XorCost =
+      TTI->getArithmeticInstrCost(Instruction::Xor, CRCTy, CostKind);
+  InstructionCost ShiftCost =
+      TTI->getArithmeticInstrCost(Instruction::LShr, CRCTy, CostKind);
+  InstructionCost AndCost =
+      TTI->getArithmeticInstrCost(Instruction::And, CRCTy, CostKind);
+  InstructionCost SelectCost =
+      TTI->getCmpSelInstrCost(Instruction::Select, CRCTy, Type::getInt1Ty(Ctx),
+                              CmpInst::BAD_ICMP_PREDICATE, CostKind);
+  auto ClmulCost = [&](unsigned BW) {
+    auto *Ty = IntegerType::get(Ctx, BW);
+    IntrinsicCostAttributes Attrs(Intrinsic::clmul, Ty, {Ty, Ty});
+    return TTI->getIntrinsicInstrCost(Attrs, CostKind);
+  };
+
+  // Determine the cost of the alternative approach to clmul.
+  InstructionCost OldCost;
+  if (TableStrategyPossible) {
+    // If the lookup table strategy is possible, it will be faster than the
+    // original loop, so compute the cost of that strategy.
+    InstructionCost CostPerTrip =
+        TTI->getMemoryOpCost(Instruction::Load, CRCTy,
+                             DL->getABITypeAlign(CRCTy),
+                             DL->getDefaultGlobalsAddressSpace(), CostKind) +
+        XorCost + 2 * ShiftCost;
+
+    // The trip count will be reduced by a factor of 8.
+    OldCost = CostPerTrip * (Info.TripCount / 8);
+    LLVM_DEBUG(dbgs() << " OldCost(table)=" << OldCost);
+  } else {
+    // Estimate the cost of the original loop.
+    InstructionCost CostPerTrip =
+        2 * ShiftCost + 2 * XorCost + AndCost + SelectCost;
+
+    OldCost = CostPerTrip * Info.TripCount;
+    LLVM_DEBUG(dbgs() << " OldCost(original)=" << OldCost);
   }
 
-  // The clmul optimization should be applied if it is fast and likely to lower
-  // in a way that keeps code small.
-  // TODO: If clmul exists on the target but not for the required width, it
-  // might be possible to split into multiple iterations of reduction.
-  unsigned ClmulMuBW = Info.IsBigEndian ? 2 * Info.TripCount : Info.TripCount;
-  unsigned ClmulGPBW =
-      Info.LHS->getType()->getIntegerBitWidth() + Info.TripCount;
-  IntegerType *WidestClmulTy =
-      IntegerType::get(Info.LHS->getContext(), std::max(ClmulMuBW, ClmulGPBW));
-  if (TTI->haveFastClmul(WidestClmulTy)) {
+  // Approximate the cost of the clmul optimization as two clmuls plus a decent
+  // conservative estimate of the other operations used.
+  InstructionCost NewCost = ClmulCost(2 * Info.TripCount) +
+                            ClmulCost(CRCBW + Info.TripCount) + 2 * XorCost +
+                            2 * ShiftCost + AndCost;
----------------
pfusik wrote:

Hasn't #210139 eliminated the `and` ?

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


More information about the llvm-commits mailing list