Skip to content

[SCEV] Add initial pattern matching for SCEV constants. (NFC) #119389

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

Merged
merged 4 commits into from
Dec 13, 2024

Conversation

fhahn
Copy link
Contributor

@fhahn fhahn commented Dec 10, 2024

Add initial pattern matching for SCEV constants. Follow-up patches will add additional matchers for various SCEV expressions.

This patch only converts a few instances to use the new matchers to make sure everything builds as expected for now.

@fhahn fhahn requested a review from nikic as a code owner December 10, 2024 15:09
@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Dec 10, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 10, 2024

@llvm/pr-subscribers-llvm-analysis

Author: Florian Hahn (fhahn)

Changes

Add initial pattern matching for SCEV constants. Follow-up patches will add additional matchers for various SCEV expressions.

This patch only converts a few instances to use the new matchers to make sure everything builds as expected for now.


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

2 Files Affected:

  • (added) llvm/include/llvm/Analysis/ScalarEvolutionPatternMatch.h (+59)
  • (modified) llvm/lib/Analysis/ScalarEvolution.cpp (+7-6)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolutionPatternMatch.h b/llvm/include/llvm/Analysis/ScalarEvolutionPatternMatch.h
new file mode 100644
index 00000000000000..636b9f8e1544f7
--- /dev/null
+++ b/llvm/include/llvm/Analysis/ScalarEvolutionPatternMatch.h
@@ -0,0 +1,59 @@
+//===- ScalarEvolutionPatternMatch.h - Match on SCEVs -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides a simple and efficient mechanism for performing general
+// tree-based pattern matches on SCEVs, based on LLVM's IR pattern matchers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ANALYSIS_SCALAREVOLUTIONPATTERNMATCH_H
+#define LLVM_ANALYSIS_SCALAREVOLUTIONPATTERNMATCH_H
+
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+
+namespace llvm {
+namespace SCEVPatternMatch {
+
+template <typename Val, typename Pattern>
+bool match(const SCEV *S, const Pattern &P) {
+  return P.match(S);
+}
+
+/// Match a specified integer value. \p BitWidth optionally specifies the
+/// bitwidth the matched constant must have. If it is 0, the matched constant
+/// can have any bitwidth.
+template <unsigned BitWidth = 0> struct specific_intval {
+  APInt Val;
+
+  specific_intval(APInt V) : Val(std::move(V)) {}
+
+  bool match(const SCEV *S) const {
+    const auto *C = dyn_cast<SCEVConstant>(S);
+    if (!C)
+      return false;
+
+    if (BitWidth != 0 && C->getAPInt().getBitWidth() != BitWidth)
+      return false;
+    return APInt::isSameValue(C->getAPInt(), Val);
+  }
+};
+
+inline specific_intval<0> m_scev_Zero() {
+  return specific_intval<0>(APInt(64, 0));
+}
+inline specific_intval<0> m_scev_One() {
+  return specific_intval<0>(APInt(64, 1));
+}
+inline specific_intval<0> m_scev_MinusOne() {
+  return specific_intval<0>(APInt(64, -1));
+}
+
+} // namespace SCEVPatternMatch
+} // namespace llvm
+
+#endif
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index cad10486cbf3fa..741431ac8aa158 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -79,6 +79,7 @@
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/Analysis/ScalarEvolutionPatternMatch.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/Config/llvm-config.h"
@@ -133,6 +134,7 @@
 
 using namespace llvm;
 using namespace PatternMatch;
+using namespace SCEVPatternMatch;
 
 #define DEBUG_TYPE "scalar-evolution"
 
@@ -3423,9 +3425,8 @@ const SCEV *ScalarEvolution::getUDivExpr(const SCEV *LHS,
     return S;
 
   // 0 udiv Y == 0
-  if (const SCEVConstant *LHSC = dyn_cast<SCEVConstant>(LHS))
-    if (LHSC->getValue()->isZero())
-      return LHS;
+  if (match(LHS, m_scev_Zero()))
+    return LHS;
 
   if (const SCEVConstant *RHSC = dyn_cast<SCEVConstant>(RHS)) {
     if (RHSC->getValue()->isOne())
@@ -10593,7 +10594,6 @@ ScalarEvolution::ExitLimit ScalarEvolution::howFarToZero(const SCEV *V,
   // Get the initial value for the loop.
   const SCEV *Start = getSCEVAtScope(AddRec->getStart(), L->getParentLoop());
   const SCEV *Step = getSCEVAtScope(AddRec->getOperand(1), L->getParentLoop());
-  const SCEVConstant *StepC = dyn_cast<SCEVConstant>(Step);
 
   if (!isLoopInvariant(Step, L))
     return getCouldNotCompute();
@@ -10615,8 +10615,8 @@ ScalarEvolution::ExitLimit ScalarEvolution::howFarToZero(const SCEV *V,
   // Handle unitary steps, which cannot wraparound.
   // 1*N = -Start; -1*N = Start (mod 2^BW), so:
   //   N = Distance (as unsigned)
-  if (StepC &&
-      (StepC->getValue()->isOne() || StepC->getValue()->isMinusOne())) {
+
+  if (match(Step, m_CombineOr(m_scev_One(), m_scev_MinusOne()))) {
     APInt MaxBECount = getUnsignedRangeMax(applyLoopGuards(Distance, Guards));
     MaxBECount = APIntOps::umin(MaxBECount, getUnsignedRangeMax(Distance));
 
@@ -10668,6 +10668,7 @@ ScalarEvolution::ExitLimit ScalarEvolution::howFarToZero(const SCEV *V,
   }
 
   // Solve the general equation.
+  const SCEVConstant *StepC = dyn_cast<SCEVConstant>(Step);
   if (!StepC || StepC->getValue()->isZero())
     return getCouldNotCompute();
   const SCEV *E = SolveLinEquationWithOverflow(

fhahn added a commit to fhahn/llvm-project that referenced this pull request Dec 10, 2024
This patch adds initial matchers for SCEV expressions with an arbitrary
number of operands and specializes it for binary add expressions.

Also adds matchers for SCEVConstant and SCEVUnknown.

This patch only converts a few instances to use the new matchers to make
sure everything builds as expected for now.

Depends on llvm#119389
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.

I like the general idea.

return specific_intval<0>(APInt(64, 1));
}
inline specific_intval<0> m_scev_MinusOne() {
return specific_intval<0>(APInt(64, -1));
Copy link
Contributor

Choose a reason for hiding this comment

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

This is going to miscompile > 64-bit due to missing sign extension. Please implement these via predicates like in PatternMatch, rather than using isSameValue and bypassing the checks that exist to prevent this mistake.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, updated to build on top of PatternMatch::specific_intval64, thanks

@fhahn fhahn force-pushed the scev-patternmatch-constants branch from dc540d1 to 5478696 Compare December 10, 2024 20:47

inline specific_intval64 m_scev_Zero() { return specific_intval64(0); }
inline specific_intval64 m_scev_One() { return specific_intval64(1); }
inline specific_intval64 m_scev_MinusOne() { return specific_intval64(-1); }
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't really address the problem. You'll still not get the correct sign extension behavior here.

What I expected to see is something along these lines:

template <typename Predicate>
struct cst_pred_ty : public Predicate {
  bool match(const SCEV *S) {
    auto *C = dyn_cast<SCEVConstant>(S);
    return C && isValue(C->getAPInt());
  }
}

inline cst_pred_ty<PatternMatch::is_all_ones> m_scev_AllOnes() {
  return cst_pred_ty<PatternMatch::is_all_ones>();
}

Copy link
Contributor

Choose a reason for hiding this comment

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

(Can also copy is_all_ones etc if we don't want the PatternMatch dependency here.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah I see, I misunderstood originally. Should be updated, thanks

@@ -0,0 +1,45 @@
//===- ScalarEvolutionPatternMatch.h - Match on SCEVs -----------*- C++ -*-===//
Copy link
Contributor

Choose a reason for hiding this comment

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

Per recent changes to guidelines, you can drop the file name and the emacs marker.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced with just ```/===----------------------------------------------------------------------===//`

@fhahn fhahn force-pushed the scev-patternmatch-constants branch from 5478696 to 881795f Compare December 11, 2024 12:01
Comment on lines 37 to 38
/// Match an integer 0 or a vector with all elements equal to 0.
/// For vectors, this includes constants with undefined elements.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Match an integer 0 or a vector with all elements equal to 0.
/// For vectors, this includes constants with undefined elements.
/// Match an integer 0.

SCEV does not operate on vectors at all.

Also, I don't think we really want the Zero/ZeroInt distinction in SCEV? If we really wanted to match "0 or null" we'd have to check for a SCEVUnknown with a null pointer constant, but I don't think this is something that we'd want to do when working on SCEV.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, simplified thanks, and added an assert that we don't encounter vector types.

@fhahn fhahn force-pushed the scev-patternmatch-constants branch from 881795f to 3f91da9 Compare December 11, 2024 13:10
}
};
/// Match any null constant.
inline is_zero m_scev_Zero() { return is_zero(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

I still think this one should use cst_pred_ty<is_zero>. Your current implementation is equivalent to doing that, in a somewhat roundabout way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated, thanks

@fhahn fhahn force-pushed the scev-patternmatch-constants branch from 3f91da9 to af03d35 Compare December 11, 2024 20:46
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.

LGTM

@fhahn fhahn merged commit 217e0f3 into llvm:main Dec 13, 2024
8 checks passed
fhahn added a commit to fhahn/llvm-project that referenced this pull request Dec 13, 2024
This patch adds initial matchers for SCEV expressions with an arbitrary
number of operands and specializes it for binary add expressions.

Also adds matchers for SCEVConstant and SCEVUnknown.

This patch only converts a few instances to use the new matchers to make
sure everything builds as expected for now.

Depends on llvm#119389
@fhahn fhahn deleted the scev-patternmatch-constants branch December 16, 2024 11:26
fhahn added a commit to fhahn/llvm-project that referenced this pull request Dec 17, 2024
This patch adds initial matchers for SCEV expressions with an arbitrary
number of operands and specializes it for binary add expressions.

Also adds matchers for SCEVConstant and SCEVUnknown.

This patch only converts a few instances to use the new matchers to make
sure everything builds as expected for now.

Depends on llvm#119389
fhahn added a commit that referenced this pull request Dec 17, 2024
This patch adds initial matchers for unary and binary SCEV expressions 
and specializes it for SExt, ZExt and binary add expressions.

Also adds matchers for SCEVConstant and SCEVUnknown.

This patch only converts a few instances to use the new matchers to make
sure everything builds as expected for now.

The goal of the matchers is to hopefully make it slightly easier to
write code matching SCEV patterns.

Depends on #119389

PR: #119390
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:analysis Includes value tracking, cost tables and constant folding
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants