Skip to content

[HashRecognize] Make it a non-PM analysis #144742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

artagnon
Copy link
Contributor

@artagnon artagnon commented Jun 18, 2025

Make HashRecognize a non-PassManager analysis that can be called to get the result on-demand, creating a new getResult() entry-point. This entry-point should also include a function_ref to generate the Sarwate table. The issue was discovered when attempting to use the analysis to perform a transform in LoopIdiomRecognize.

@artagnon artagnon requested review from nikic and pfusik June 18, 2025 16:11
@llvmbot
Copy link
Member

llvmbot commented Jun 18, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Ramkumar Ramachandra (artagnon)

Changes

Make the analysis return a proper analysis result that can be cached, instead of a HashRecognize object which has to be called to perform the actual analysis. This also means that we need a function_ref to generate the Sarwate table returned along with the analysis result. The issue was discovered when attempting to use the analysis to perform a transform.


Full diff: https://github.com/llvm/llvm-project/pull/144742.diff

2 Files Affected:

  • (modified) llvm/include/llvm/Analysis/HashRecognize.h (+6-5)
  • (modified) llvm/lib/Analysis/HashRecognize.cpp (+18-12)
diff --git a/llvm/include/llvm/Analysis/HashRecognize.h b/llvm/include/llvm/Analysis/HashRecognize.h
index 8ab68a5dc2cb1..8aa1e0e788f99 100644
--- a/llvm/include/llvm/Analysis/HashRecognize.h
+++ b/llvm/include/llvm/Analysis/HashRecognize.h
@@ -66,6 +66,10 @@ struct PolynomialInfo {
   // Set to true in the case of big-endian.
   bool ByteOrderSwapped;
 
+  // A function_ref to generate the Sarwate lookup-table, which can be used to
+  // optimize CRC in the absence of target-specific instructions.
+  function_ref<CRCTable(const APInt &, bool)> GenSarwateTable;
+
   // An optional auxiliary checksum that augments the LHS. In the case of CRC,
   // it is XOR'ed with the LHS, so that the computation's final remainder is
   // zero.
@@ -73,6 +77,7 @@ struct PolynomialInfo {
 
   PolynomialInfo(unsigned TripCount, const Value *LHS, const APInt &RHS,
                  const Value *ComputedValue, bool ByteOrderSwapped,
+                 function_ref<CRCTable(const APInt &, bool)> GenSarwateTable,
                  const Value *LHSAux = nullptr);
 };
 
@@ -87,10 +92,6 @@ class HashRecognize {
   // The main analysis entry point.
   std::variant<PolynomialInfo, ErrBits, StringRef> recognizeCRC() const;
 
-  // Auxilary entry point after analysis to interleave the generating polynomial
-  // and return a 256-entry CRC table.
-  CRCTable genSarwateTable(const APInt &GenPoly, bool ByteOrderSwapped) const;
-
   void print(raw_ostream &OS) const;
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -113,7 +114,7 @@ class HashRecognizeAnalysis : public AnalysisInfoMixin<HashRecognizeAnalysis> {
   static AnalysisKey Key;
 
 public:
-  using Result = HashRecognize;
+  using Result = std::optional<PolynomialInfo>;
   Result run(Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR);
 };
 } // namespace llvm
diff --git a/llvm/lib/Analysis/HashRecognize.cpp b/llvm/lib/Analysis/HashRecognize.cpp
index 1edb8b3bdc9a8..83a0c36451949 100644
--- a/llvm/lib/Analysis/HashRecognize.cpp
+++ b/llvm/lib/Analysis/HashRecognize.cpp
@@ -442,11 +442,14 @@ getRecurrences(BasicBlock *LoopLatch, const PHINode *IndVar, const Loop &L) {
   return std::make_pair(SimpleRecurrence, ConditionalRecurrence);
 }
 
-PolynomialInfo::PolynomialInfo(unsigned TripCount, const Value *LHS,
-                               const APInt &RHS, const Value *ComputedValue,
-                               bool ByteOrderSwapped, const Value *LHSAux)
+PolynomialInfo::PolynomialInfo(
+    unsigned TripCount, const Value *LHS, const APInt &RHS,
+    const Value *ComputedValue, bool ByteOrderSwapped,
+    function_ref<CRCTable(const APInt &, bool)> GenSarwateTable,
+    const Value *LHSAux)
     : TripCount(TripCount), LHS(LHS), RHS(RHS), ComputedValue(ComputedValue),
-      ByteOrderSwapped(ByteOrderSwapped), LHSAux(LHSAux) {}
+      ByteOrderSwapped(ByteOrderSwapped), GenSarwateTable(GenSarwateTable),
+      LHSAux(LHSAux) {}
 
 /// In the big-endian case, checks the bottom N bits against CheckFn, and that
 /// the rest are unknown. In the little-endian case, checks the top N bits
@@ -471,8 +474,7 @@ static bool checkExtractBits(const KnownBits &Known, unsigned N,
 /// Generate a lookup table of 256 entries by interleaving the generating
 /// polynomial. The optimization technique of table-lookup for CRC is also
 /// called the Sarwate algorithm.
-CRCTable HashRecognize::genSarwateTable(const APInt &GenPoly,
-                                        bool ByteOrderSwapped) const {
+static CRCTable genSarwateTable(const APInt &GenPoly, bool ByteOrderSwapped) {
   unsigned BW = GenPoly.getBitWidth();
   CRCTable Table;
   Table[0] = APInt::getZero(BW);
@@ -625,7 +627,7 @@ HashRecognize::recognizeCRC() const {
 
   const Value *LHSAux = SimpleRecurrence ? SimpleRecurrence.Start : nullptr;
   return PolynomialInfo(TC, ConditionalRecurrence.Start, GenPoly, ComputedValue,
-                        *ByteOrderSwapped, LHSAux);
+                        *ByteOrderSwapped, genSarwateTable, LHSAux);
 }
 
 void CRCTable::print(raw_ostream &OS) const {
@@ -679,7 +681,7 @@ void HashRecognize::print(raw_ostream &OS) const {
     OS << "\n";
   }
   OS.indent(2) << "Computed CRC lookup table:\n";
-  genSarwateTable(Info.RHS, Info.ByteOrderSwapped).print(OS);
+  Info.GenSarwateTable(Info.RHS, Info.ByteOrderSwapped).print(OS);
 }
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -693,13 +695,17 @@ PreservedAnalyses HashRecognizePrinterPass::run(Loop &L,
                                                 LoopAnalysisManager &AM,
                                                 LoopStandardAnalysisResults &AR,
                                                 LPMUpdater &) {
-  AM.getResult<HashRecognizeAnalysis>(L, AR).print(OS);
+  HashRecognize(L, AR.SE).print(OS);
   return PreservedAnalyses::all();
 }
 
-HashRecognize HashRecognizeAnalysis::run(Loop &L, LoopAnalysisManager &AM,
-                                         LoopStandardAnalysisResults &AR) {
-  return {L, AR.SE};
+std::optional<PolynomialInfo>
+HashRecognizeAnalysis::run(Loop &L, LoopAnalysisManager &AM,
+                           LoopStandardAnalysisResults &AR) {
+  auto Res = HashRecognize(L, AR.SE).recognizeCRC();
+  if (std::holds_alternative<PolynomialInfo>(Res))
+    return std::get<PolynomialInfo>(Res);
+  return std::nullopt;
 }
 
 AnalysisKey HashRecognizeAnalysis::Key;

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered going the other way around and not making this a PM-level analysis at all? You can just locally instantiate the object in the single place you're going to use it. I don't think you gain anything but unwelcome problems (like: did the results potentially get invalidated by a LIR transform that ran earlier?)

@artagnon
Copy link
Contributor Author

Have you considered going the other way around and not making this a PM-level analysis at all? You can just locally instantiate the object in the single place you're going to use it. I don't think you gain anything but unwelcome problems (like: did the results potentially get invalidated by a LIR transform that ran earlier?)

I thought about that, yes. I did lean towards making it a PM-level analysis for ease of testing. I leverage the existing infrastructure in update_analyze_test_checks to auto-generate the result, with a simple opt invocation in the RUN line. If this weren't a PM-level analysis, I'd have to hand-craft unit-tests in C++.

I think the specific problem you mentioned can be easily tackled by not preserving the analysis result in LIR. I don't think LIR runs more than once anyway, so I don't think it's ever computed more than once.

Besides, I plan to grow HashRecognize in size by also including other polynomial hashes, and possibly requiring more analysis results. In that sense, I think it is less like ConstraintSystem, and more like an independent analysis.

@artagnon
Copy link
Contributor Author

I thought about that, yes. I did lean towards making it a PM-level analysis for ease of testing. I leverage the existing infrastructure in update_analyze_test_checks to auto-generate the result, with a simple opt invocation in the RUN line. If this weren't a PM-level analysis, I'd have to hand-craft unit-tests in C++.

I think the specific problem you mentioned can be easily tackled by not preserving the analysis result in LIR. I don't think LIR runs more than once anyway, so I don't think it's ever computed more than once.

Besides, I plan to grow HashRecognize in size by also including other polynomial hashes, and possibly requiring more analysis results. In that sense, I think it is less like ConstraintSystem, and more like an independent analysis.

Actually, for testing, I only need the Printer pass. I thought about it some more, and I think it would make sense to strip the analysis pass entirely, and just pass SE and L for now. If we need more things, we can always get it in LIR, and pass it: I don't expect this to be used outside LIR anyway.

@artagnon artagnon changed the title [HashRecognize] Fix the analysis result [HashRecognize] Make it a non-PM analysis Jun 18, 2025
@artagnon artagnon force-pushed the hr-analysisresult branch from b2eca1d to 675cf86 Compare June 18, 2025 19:56
Copy link

github-actions bot commented Jun 18, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Make the analysis return a proper analysis result that can be cached,
instead of a HashRecognize object which has to be called to perform the
actual analysis. This also means that we need a function_ref to generate
the Sarwate table returned along with the analysis result. The issue was
discovered when attempting to use the analysis to perform a transform.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants