Skip to content

[DAG] SDPatternMatch - add m_Negative/m_StrictlyPositive/m_NonNegative/m_NonPositive/m_NonZero matchers#175191

Merged
RKSimon merged 5 commits intollvm:mainfrom
ak1932:DAG_matchers
Jan 22, 2026
Merged

[DAG] SDPatternMatch - add m_Negative/m_StrictlyPositive/m_NonNegative/m_NonPositive/m_NonZero matchers#175191
RKSimon merged 5 commits intollvm:mainfrom
ak1932:DAG_matchers

Conversation

@ak1932
Copy link
Contributor

@ak1932 ak1932 commented Jan 9, 2026

wip: Still need to find a fold in DAGCombiner and add more extensive unit testing.

Resolves #174327

@github-actions
Copy link

github-actions bot commented Jan 9, 2026

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the llvm:SelectionDAG SelectionDAGISel as well label Jan 9, 2026
@ak1932
Copy link
Contributor Author

ak1932 commented Jan 9, 2026

@RKSimon, Am i on the right track?

@llvmbot
Copy link
Member

llvmbot commented Jan 9, 2026

@llvm/pr-subscribers-llvm-selectiondag

Author: Aryan Kadole (ak1932)

Changes

wip: Still need to find a fold in DAGCombiner and add more extensive unit testing.

Resolves #174327


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

4 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/SDPatternMatch.h (+55)
  • (modified) llvm/include/llvm/CodeGen/SelectionDAG.h (+15)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp (+44)
  • (modified) llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp (+16)
diff --git a/llvm/include/llvm/CodeGen/SDPatternMatch.h b/llvm/include/llvm/CodeGen/SDPatternMatch.h
index 026ee035fcf54..6cf3bb115448e 100644
--- a/llvm/include/llvm/CodeGen/SDPatternMatch.h
+++ b/llvm/include/llvm/CodeGen/SDPatternMatch.h
@@ -1173,6 +1173,46 @@ inline SpecificFP_match m_SpecificFP(double V) {
   return SpecificFP_match(APFloat(V));
 }
 
+struct Negative_match {
+  template <typename MatchContext>
+  bool match(const MatchContext &M, SDValue N) const {
+    const SelectionDAG *DAG = M.getDAG();
+    return DAG ? DAG->IsNegative(N) : false;
+  }
+};
+
+struct NonNegative_match {
+  template <typename MatchContext>
+  bool match(const MatchContext &M, SDValue N) const {
+    const SelectionDAG *DAG = M.getDAG();
+    return DAG ? DAG->IsNonNegative(N) : false;
+  }
+};
+
+struct StrictlyPositive_match {
+  template <typename MatchContext>
+  bool match(const MatchContext &M, SDValue N) const {
+    const SelectionDAG *DAG = M.getDAG();
+    return DAG ? DAG->IsStrictlyPositive(N) : false;
+  }
+};
+
+struct NonPositive_match {
+  template <typename MatchContext>
+  bool match(const MatchContext &M, SDValue N) const {
+    const SelectionDAG *DAG = M.getDAG();
+    return DAG ? DAG->IsNonPositive(N) : false;
+  }
+};
+
+struct NonZero_match {
+  template <typename MatchContext>
+  bool match(const MatchContext &M, SDValue N) const {
+    const SelectionDAG *DAG = M.getDAG();
+    return DAG ? DAG->IsNonZero(N) : false;
+  }
+};
+
 struct Zero_match {
   bool AllowUndefs;
 
@@ -1204,6 +1244,21 @@ struct AllOnes_match {
   }
 };
 
+inline Negative_match m_Negative() {
+  return Negative_match();
+}
+inline NonNegative_match m_NonNegative() {
+  return NonNegative_match();
+}
+inline StrictlyPositive_match m_StrictlyPositive() {
+  return StrictlyPositive_match();
+}
+inline NonPositive_match m_NonPositive() {
+  return NonPositive_match();
+}
+inline NonZero_match m_NonZero() {
+  return NonZero_match();
+}
 inline Ones_match m_One(bool AllowUndefs = false) {
   return Ones_match(AllowUndefs);
 }
diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index 4c7c9942fb8f6..4c760ee79cc41 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -2077,6 +2077,21 @@ class SelectionDAG {
   /// floating-point value.
   LLVM_ABI bool SignBitIsZeroFP(SDValue Op, unsigned Depth = 0) const;
 
+  /// Return true if Op is strictly positive FP or integer
+  LLVM_ABI bool IsStrictlyPositive(SDValue Op, unsigned Depth = 0) const;
+
+  /// Return true if Op is non positive FP or integer
+  LLVM_ABI bool IsNonPositive(SDValue Op, unsigned Depth = 0) const;
+
+  /// Return true if Op is a Negative FP or integer
+  LLVM_ABI bool IsNegative(SDValue Op, unsigned Depth = 0) const;
+
+  /// Return true if Op is non negative
+  LLVM_ABI bool IsNonNegative(SDValue Op, unsigned Depth = 0) const;
+
+  /// Return true if Op is non zero
+  LLVM_ABI bool IsNonZero(SDValue Op, unsigned Depth = 0) const;
+
   /// Return true if 'Op & Mask' is known to be zero.  We
   /// use this predicate to simplify operations downstream.  Op and Mask are
   /// known to be the same type.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 891f584cf0c3a..fd848633f9fdc 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -2834,6 +2834,50 @@ bool SelectionDAG::SignBitIsZeroFP(SDValue Op, unsigned Depth) const {
   llvm_unreachable("covered opcode switch");
 }
 
+bool SelectionDAG::IsNegative(SDValue Op, unsigned Depth) const {
+  if (Op.getValueType().isScalarInteger()
+      || Op.getValueType().isFloatingPoint()) {
+    KnownBits KB = computeKnownBits(Op);
+    return KB.isNegative();
+  }
+
+  return false;
+}
+
+bool SelectionDAG::IsNonNegative(SDValue Op, unsigned Depth) const {
+  if (Op.getValueType().isScalarInteger()
+      || Op.getValueType().isFloatingPoint()) {
+    KnownBits KB = computeKnownBits(Op);
+    return KB.isNonNegative();
+  }
+
+  return false;
+}
+
+bool SelectionDAG::IsStrictlyPositive(SDValue Op, unsigned Depth) const {
+  if (Op.getValueType().isScalarInteger()
+      || Op.getValueType().isFloatingPoint()) {
+    KnownBits KB = this->computeKnownBits(Op);
+    return KB.isStrictlyPositive();
+  }
+
+  return false;
+}
+
+bool SelectionDAG::IsNonPositive(SDValue Op, unsigned Depth) const {
+  return !IsStrictlyPositive(Op);
+}
+
+bool SelectionDAG::IsNonZero(SDValue Op, unsigned Depth) const {
+  if (Op.getValueType().isScalarInteger()
+      || Op.getValueType().isFloatingPoint()) {
+    KnownBits KB = computeKnownBits(Op);
+    return KB.isNonZero();
+  }
+
+  return false;
+}
+
 /// MaskedValueIsZero - Return true if 'V & Mask' is known to be zero.  We use
 /// this predicate to simplify operations downstream.  Mask is known to be zero
 /// for bits that V cannot have.
diff --git a/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp b/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp
index 1afc034dd7b9e..8d2e8627f1d9f 100644
--- a/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp
+++ b/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp
@@ -556,6 +556,7 @@ TEST_F(SelectionDAGPatternMatchTest, matchConstants) {
   SDValue ConstSplat = DAG->getSplat(VInt32VT, DL, Const3);
   SDValue Zero = DAG->getConstant(0, DL, Int32VT);
   SDValue One = DAG->getConstant(1, DL, Int32VT);
+  SDValue MinusOne = DAG->getConstant(APInt(Int32VT.getScalarSizeInBits(), -1, true), DL, Int32VT);
   SDValue AllOnes = DAG->getConstant(APInt::getAllOnes(32), DL, Int32VT);
   SDValue SetCC = DAG->getSetCC(DL, Int32VT, Arg0, Const3, ISD::SETULT);
 
@@ -575,6 +576,21 @@ TEST_F(SelectionDAGPatternMatchTest, matchConstants) {
   EXPECT_TRUE(sd_match(One, DAG.get(), m_True()));
   EXPECT_FALSE(sd_match(AllOnes, DAG.get(), m_True()));
 
+  EXPECT_TRUE (sd_match(MinusOne, DAG.get(), m_Negative()));
+  EXPECT_TRUE (sd_match(MinusOne, DAG.get(), m_NonZero()));
+  EXPECT_TRUE (sd_match(MinusOne, DAG.get(), m_NonPositive()));
+  EXPECT_FALSE(sd_match(MinusOne, DAG.get(), m_StrictlyPositive()));
+
+  EXPECT_FALSE(sd_match(Zero, DAG.get(), m_Negative()));
+  EXPECT_FALSE(sd_match(Zero, DAG.get(), m_NonZero()));
+  EXPECT_TRUE (sd_match(Zero, DAG.get(), m_NonPositive()));
+  EXPECT_FALSE(sd_match(Zero, DAG.get(), m_StrictlyPositive()));
+
+  EXPECT_FALSE(sd_match(One, DAG.get(), m_Negative()));
+  EXPECT_TRUE (sd_match(One, DAG.get(), m_NonZero()));
+  EXPECT_FALSE(sd_match(One, DAG.get(), m_NonPositive()));
+  EXPECT_TRUE (sd_match(One, DAG.get(), m_StrictlyPositive()));
+
   ISD::CondCode CC;
   EXPECT_TRUE(sd_match(
       SetCC, m_Node(ISD::SETCC, m_Value(), m_Value(), m_CondCode(CC))));

@ak1932 ak1932 marked this pull request as draft January 9, 2026 15:58
@RKSimon RKSimon requested review from RKSimon and mshockwave January 9, 2026 16:56
template <typename MatchContext>
bool match(const MatchContext &M, SDValue N) const {
const SelectionDAG *DAG = M.getDAG();
return DAG ? DAG->IsNegative(N) : false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we need new SelectionDAG helpers for these, try:

Suggested change
return DAG ? DAG->IsNegative(N) : false;
return DAG && DAG->computeKnownBits(N).IsNegative() ;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was also checking whether the SDValue corresponds to a Scalar Integer or Floating point in the helper functions. Would that be necessary or this would suffice?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not necessary, we want this to work for vectors as well - computeKnownBits is your friend :)

template <typename MatchContext>
bool match(const MatchContext &M, SDValue N) const {
const SelectionDAG *DAG = M.getDAG();
return DAG ? DAG->IsNonNegative(N) : false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return DAG ? DAG->IsNonNegative(N) : false;
return DAG && DAG->IsNonNegative(N);

template <typename MatchContext>
bool match(const MatchContext &M, SDValue N) const {
const SelectionDAG *DAG = M.getDAG();
return DAG ? DAG->IsStrictlyPositive(N) : false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return DAG ? DAG->IsStrictlyPositive(N) : false;
return DAG && DAG->IsStrictlyPositive(N);

template <typename MatchContext>
bool match(const MatchContext &M, SDValue N) const {
const SelectionDAG *DAG = M.getDAG();
return DAG ? DAG->IsNonPositive(N) : false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might need to drop this one - not sure

Copy link
Collaborator

Choose a reason for hiding this comment

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

no you don't - we now have IsNonPositive :)

template <typename MatchContext>
bool match(const MatchContext &M, SDValue N) const {
const SelectionDAG *DAG = M.getDAG();
return DAG ? DAG->IsNonZero(N) : false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return DAG ? DAG->IsNonZero(N) : false;
return DAG && DAG->IsNonZeroN);

@@ -1213,6 +1244,21 @@ struct AllOnes_match {
}
};

inline Negative_match m_Negative() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need captures for these, I need to be able to do the likes of:

sd_match(N, m_Sub(m_Value(X),m_Negative(m_Value(Y)))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

Something like this should work (untested):

template <typename ValTy>
inline auto m_Negative(const ValTy &V) {
  return m_AllOf(m_Negative(), V);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Something like this should work (untested):

template <typename ValTy>
inline auto m_Negative(const ValTy &V) {
  return m_AllOf(m_Negative(), V);
}

Thanks! I had also worked out something similar but couldn't get it to work with Value_match() and Value_bind() both. This worked out great!

LLVM_ABI bool IsNonNegative(SDValue Op, unsigned Depth = 0) const;

/// Return true if Op is non zero
LLVM_ABI bool IsNonZero(SDValue Op, unsigned Depth = 0) const;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Drop all these

Copy link
Collaborator

Choose a reason for hiding this comment

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

Test with non-constants as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure 👍

@github-actions
Copy link

github-actions bot commented Jan 9, 2026

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

template <typename MatchContext>
bool match(const MatchContext &M, SDValue N) const {
const SelectionDAG *DAG = M.getDAG();
return DAG ? DAG->IsNonPositive(N) : false;
Copy link
Collaborator

@RKSimon RKSimon Jan 12, 2026

Choose a reason for hiding this comment

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

Suggested change
return DAG ? DAG->IsNonPositive(N) : false;
return DAG && DAG->IsNonPositive(N);

@ak1932
Copy link
Contributor Author

ak1932 commented Jan 13, 2026

@RKSimon Sorry for the delay, I was a bit caught up with school and internship work, and couldn't devote much time to this issue / PR, the previous week. I have now made the changes requested and also rebased to make use of isNonPositive() helper in KnownBits.

@RKSimon RKSimon marked this pull request as ready for review January 14, 2026 14:56
Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

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

Please can you add a couple of tests case where DAG has not been set inside sd_match - just to ensure that we don't attempt deref a nullptr to DAG

@ak1932 ak1932 force-pushed the DAG_matchers branch 3 times, most recently from 67903a8 to cfb843b Compare January 14, 2026 16:11
@ak1932
Copy link
Contributor Author

ak1932 commented Jan 14, 2026

Also, in the default configuration all tests in SelectionDAGPatternMatchTest were skipped, so I had to change the TargetTriple from aarch64 to x86_64 which is my machine. Should I have committed that as well? I think by default the TargetTriple should probably be fetched from the machine or as the FIXME comment says, should have a skeleton target

Triple TargetTriple("aarch64--");

@RKSimon
Copy link
Collaborator

RKSimon commented Jan 14, 2026

what targets are you building in cmake?

@ak1932
Copy link
Contributor Author

ak1932 commented Jan 14, 2026

what targets are you building in cmake?

Only X86 with RelWithDebInfo build type

cmake -S llvm -B build -G Ninja -DLLVM_USE_LINKER=lld -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_TARGETS_TO_BUILD=X86 -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLLVM_ENABLE_PROJECTS='lld'

@RKSimon
Copy link
Collaborator

RKSimon commented Jan 14, 2026

Yes, you will need to add AARCH64 to your LLVM_TARGETS_TO_BUILD list as well - don't change the unit test triple

Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

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

@ak1932 please can you fix the clang-format CI warning

@ak1932
Copy link
Contributor Author

ak1932 commented Jan 16, 2026

@ak1932 please can you fix the clang-format CI warning

I think I have already formatted my code, will check again.

@RKSimon
Copy link
Collaborator

RKSimon commented Jan 16, 2026

Take a look at the CI warning

@ak1932
Copy link
Contributor Author

ak1932 commented Jan 16, 2026

I checked again, and it did require some formatting 😅 Will pay attention to CI for any issues next time 👍
Thanks!

@RKSimon RKSimon self-requested a review January 16, 2026 18:06
SDValue SignBit = DAG->getConstant(0x80000000u, DL, Int32VT);
SDValue LSB = DAG->getConstant(0x00000001u, DL, Int32VT);
// ~SignBit
SDValue NotSignBit = DAG->getNode(
Copy link
Collaborator

Choose a reason for hiding this comment

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

DAG.getNOT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, right 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably a silly question; as a code reviewer, do you prefer amending existing commits and force pushing or making new commits?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Extra commits are better - avoid rebase and force push whenever possible to retain comment history

Copy link
Collaborator

Choose a reason for hiding this comment

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

Extra commits are better - avoid rebase and force push whenever possible to retain comment history

@RKSimon RKSimon self-requested a review January 21, 2026 21:29
@RKSimon
Copy link
Collaborator

RKSimon commented Jan 21, 2026

@ak1932 If you're after a use case in DAGCombine - the HasKnownSameSign lambda inside visitIMINMAX might be a good option

Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

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

Missing a DAGCombine usage (if we can)

@ak1932
Copy link
Contributor Author

ak1932 commented Jan 22, 2026

Yeah, the issue that I faced with finding a fold was that any fold I could think of was already performed using DAG.SignBitIsZero and such variants. I will check the lambda you mentioned 👍

@ak1932
Copy link
Contributor Author

ak1932 commented Jan 22, 2026

HasKnownSameSign

    auto HasKnownSameSign = [&](SDValue A, SDValue B) {
      if (A.isUndef() || B.isUndef())
        return true;

      KnownBits KA = DAG.computeKnownBits(A);
      if (!KA.isNonNegative() && !KA.isNegative())
        return false;

      KnownBits KB = DAG.computeKnownBits(B);
      if (KA.isNonNegative())
        return KB.isNonNegative();
      return KB.isNegative();
    };

    if (HasKnownSameSign(N0, N1)) {

Here, HasKnownSameSign lambda uses computeKnownBits once per value. To replace it with the new matchers we would use something like this

    if (sd_match(N, &DAG, m_BinOp(Opcode, m_NonPositive(), m_NonPositive()))
        || sd_match(N, &DAG, m_BinOp(Opcode, m_NonNegative(), m_NonNegative()))) {

However, it would use computeKnownBits twice per value in non negative cases.

Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

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

LGTM - cheers - we'll add DAG combine coverage as it comes along

@RKSimon RKSimon enabled auto-merge (squash) January 22, 2026 13:41
@RKSimon RKSimon merged commit af4f5ee into llvm:main Jan 22, 2026
10 of 11 checks passed
@llvm-ci
Copy link

llvm-ci commented Jan 22, 2026

LLVM Buildbot has detected a new failure on builder libc-x86_64-debian running on libc-x86_64-debian while building llvm at step 2 "update-annotated-scripts".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/43/builds/37312

Here is the relevant piece of the build log for the reference
Step 2 (update-annotated-scripts) failure: update (failure)
git version 2.39.5
remote: Internal Server Error
fatal: unable to access 'https://github.com/llvm/llvm-zorg.git/': The requested URL returned error: 500
remote: Internal Server Error
fatal: unable to access 'https://github.com/llvm/llvm-zorg.git/': The requested URL returned error: 500

Harrish92 pushed a commit to Harrish92/llvm-project that referenced this pull request Jan 23, 2026
Harrish92 pushed a commit to Harrish92/llvm-project that referenced this pull request Jan 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:SelectionDAG SelectionDAGISel as well

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DAG] SDPatternMatch - add m_Negative/m_StrictlyPositive/m_NonNegative/m_NonPositive/m_NonZero matchers

4 participants