Skip to content

[ConstraintElim] NE implies SLT if SLE and SGT if SGE #127663

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

Closed
wants to merge 2 commits into from

Conversation

citymarina
Copy link
Contributor

@llvmbot
Copy link
Member

llvmbot commented Feb 18, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Marina Taylor (citymarina)

Changes

https://alive2.llvm.org/ce/z/bsa7t5


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+26-14)
  • (modified) llvm/test/Transforms/ConstraintElimination/ne.ll (+81)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 6dd26910f6846..48a7a58ec9e9c 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1086,6 +1086,20 @@ void State::addInfoForInductions(BasicBlock &BB) {
 void State::addInfoFor(BasicBlock &BB) {
   addInfoForInductions(BB);
 
+  auto addConditionFact = [&](DomTreeNode *DTN, CmpPredicate Pred, Value *Op0,
+                              Value *Op1) {
+    WorkList.emplace_back(FactOrCheck::getConditionFact(DTN, Pred, Op0, Op1));
+    // In the case of NE zero, we can deduce SLT if SLE and SGT if SGE.
+    if (Pred == CmpInst::ICMP_NE && match(Op1, m_Zero())) {
+      ConditionTy Precond = {CmpInst::ICMP_SLE, Op0, Op1};
+      WorkList.emplace_back(FactOrCheck::getConditionFact(
+          DTN, CmpInst::ICMP_SLT, Op0, Op1, Precond));
+      Precond = {CmpInst::ICMP_SGE, Op0, Op1};
+      WorkList.emplace_back(FactOrCheck::getConditionFact(
+          DTN, CmpInst::ICMP_SGT, Op0, Op1, Precond));
+    }
+  };
+
   // True as long as long as the current instruction is guaranteed to execute.
   bool GuaranteedToExecute = true;
   // Queue conditions and assumes.
@@ -1112,8 +1126,7 @@ void State::addInfoFor(BasicBlock &BB) {
       if (GuaranteedToExecute) {
         // The assume is guaranteed to execute when BB is entered, hence Cond
         // holds on entry to BB.
-        WorkList.emplace_back(FactOrCheck::getConditionFact(
-            DT.getNode(I.getParent()), Pred, A, B));
+        addConditionFact(DT.getNode(I.getParent()), Pred, A, B);
       } else {
         WorkList.emplace_back(
             FactOrCheck::getInstFact(DT.getNode(I.getParent()), &I));
@@ -1154,8 +1167,8 @@ void State::addInfoFor(BasicBlock &BB) {
       Value *V = Case.getCaseValue();
       if (!canAddSuccessor(BB, Succ))
         continue;
-      WorkList.emplace_back(FactOrCheck::getConditionFact(
-          DT.getNode(Succ), CmpInst::ICMP_EQ, Switch->getCondition(), V));
+      addConditionFact(DT.getNode(Succ), CmpInst::ICMP_EQ,
+                       Switch->getCondition(), V);
     }
     return;
   }
@@ -1191,10 +1204,10 @@ void State::addInfoFor(BasicBlock &BB) {
       while (!CondWorkList.empty()) {
         Value *Cur = CondWorkList.pop_back_val();
         if (auto *Cmp = dyn_cast<ICmpInst>(Cur)) {
-          WorkList.emplace_back(FactOrCheck::getConditionFact(
-              DT.getNode(Successor),
-              IsOr ? Cmp->getInverseCmpPredicate() : Cmp->getCmpPredicate(),
-              Cmp->getOperand(0), Cmp->getOperand(1)));
+          addConditionFact(DT.getNode(Successor),
+                           IsOr ? Cmp->getInverseCmpPredicate()
+                                : Cmp->getCmpPredicate(),
+                           Cmp->getOperand(0), Cmp->getOperand(1));
           continue;
         }
         if (IsOr && match(Cur, m_LogicalOr(m_Value(Op0), m_Value(Op1)))) {
@@ -1216,13 +1229,12 @@ void State::addInfoFor(BasicBlock &BB) {
   if (!CmpI)
     return;
   if (canAddSuccessor(BB, Br->getSuccessor(0)))
-    WorkList.emplace_back(FactOrCheck::getConditionFact(
-        DT.getNode(Br->getSuccessor(0)), CmpI->getCmpPredicate(),
-        CmpI->getOperand(0), CmpI->getOperand(1)));
+    addConditionFact(DT.getNode(Br->getSuccessor(0)), CmpI->getCmpPredicate(),
+                     CmpI->getOperand(0), CmpI->getOperand(1));
   if (canAddSuccessor(BB, Br->getSuccessor(1)))
-    WorkList.emplace_back(FactOrCheck::getConditionFact(
-        DT.getNode(Br->getSuccessor(1)), CmpI->getInverseCmpPredicate(),
-        CmpI->getOperand(0), CmpI->getOperand(1)));
+    addConditionFact(DT.getNode(Br->getSuccessor(1)),
+                     CmpI->getInverseCmpPredicate(), CmpI->getOperand(0),
+                     CmpI->getOperand(1));
 }
 
 #ifndef NDEBUG
diff --git a/llvm/test/Transforms/ConstraintElimination/ne.ll b/llvm/test/Transforms/ConstraintElimination/ne.ll
index 4753860db2851..6413b398e70f5 100644
--- a/llvm/test/Transforms/ConstraintElimination/ne.ll
+++ b/llvm/test/Transforms/ConstraintElimination/ne.ll
@@ -423,3 +423,84 @@ define i1 @assume_4b(i64 %a, i64 %b) {
   %ret = icmp sle i64 %a, %b
   ret i1 %ret
 }
+
+define i1 @sle_and_ne_imply_slt(i8 %a) {
+; CHECK-LABEL: @sle_and_ne_imply_slt(
+; CHECK-NEXT:    [[SLE:%.*]] = icmp sle i8 [[A:%.*]], 0
+; CHECK-NEXT:    br i1 [[SLE]], label [[LESS_OR_EQUAL:%.*]], label [[EXIT:%.*]]
+; CHECK:       less_or_equal:
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i8 [[A]], 0
+; CHECK-NEXT:    br i1 [[NE]], label [[NOT_EQUAL:%.*]], label [[EXIT]]
+; CHECK:       not_equal:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       exit:
+; CHECK-NEXT:    ret i1 false
+;
+  %sle = icmp sle i8 %a, 0
+  br i1 %sle, label %less_or_equal, label %exit
+
+less_or_equal:
+  %ne = icmp ne i8 %a, 0
+  br i1 %ne, label %not_equal, label %exit
+
+not_equal:
+  %slt = icmp slt i8 %a, 0
+  ret i1 %slt
+
+exit:
+  ret i1 0
+}
+
+define i1 @sge_and_ne_imply_sgt(i8 %a) {
+; CHECK-LABEL: @sge_and_ne_imply_sgt(
+; CHECK-NEXT:    [[SGE:%.*]] = icmp sge i8 [[A:%.*]], 0
+; CHECK-NEXT:    br i1 [[SGE]], label [[GREATER_OR_EQUAL:%.*]], label [[EXIT:%.*]]
+; CHECK:       greater_or_equal:
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i8 [[A]], 0
+; CHECK-NEXT:    br i1 [[NE]], label [[NOT_EQUAL:%.*]], label [[EXIT]]
+; CHECK:       not_equal:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       exit:
+; CHECK-NEXT:    ret i1 false
+;
+  %sge = icmp sge i8 %a, 0
+  br i1 %sge, label %greater_or_equal, label %exit
+
+greater_or_equal:
+  %ne = icmp ne i8 %a, 0
+  br i1 %ne, label %not_equal, label %exit
+
+not_equal:
+  %sgt = icmp sgt i8 %a, 0
+  ret i1 %sgt
+
+exit:
+  ret i1 0
+}
+
+define i1 @sge_and_ne_imply_sgt_opposite_successor(i8 %a) {
+; CHECK-LABEL: @sge_and_ne_imply_sgt_opposite_successor(
+; CHECK-NEXT:    [[SLT:%.*]] = icmp slt i8 [[A:%.*]], 0
+; CHECK-NEXT:    br i1 [[SLT]], label [[EXIT:%.*]], label [[GREATER_OR_EQUAL:%.*]]
+; CHECK:       greater_or_equal:
+; CHECK-NEXT:    [[EQ:%.*]] = icmp eq i8 [[A]], 0
+; CHECK-NEXT:    br i1 [[EQ]], label [[EXIT]], label [[NOT_EQUAL:%.*]]
+; CHECK:       not_equal:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       exit:
+; CHECK-NEXT:    ret i1 false
+;
+  %slt = icmp slt i8 %a, 0
+  br i1 %slt, label %exit, label %greater_or_equal
+
+greater_or_equal:
+  %eq = icmp eq i8 %a, 0
+  br i1 %eq, label %exit, label %not_equal
+
+not_equal:
+  %sgt = icmp sgt i8 %a, 0
+  ret i1 %sgt
+
+exit:
+  ret i1 0
+}

@nikic
Copy link
Contributor

nikic commented Feb 18, 2025

@dtcxzyw
Copy link
Member

dtcxzyw commented Feb 19, 2025

The motivating case has been handled by CVP/SCCP: https://godbolt.org/z/qE9srErGx
I am drafting a patch in LVI to perform missed optimizations exposed by this PR.

@citymarina
Copy link
Contributor Author

Thank you both for the input. It sounds like we don't need this patch for now. I've opened #128168 to cover a different use case that we have.

@citymarina citymarina closed this Feb 21, 2025
dtcxzyw added a commit that referenced this pull request Feb 23, 2025
We cannot infer more information from backedges in
`solveBlockValueNonLocal`. However, since DT is unavailable in LVI,
there is not a precise way to check whether a BB edge is a backedge.
This patch only skips self loops to unblock the range analysis.

The motivating case is extracted from
#127663.

Compile-time impact is high:
https://llvm-compile-time-tracker.com/compare.php?from=84ddda58c870681dd12ed765e9d59d5e00567f94&to=af032f1351358f2f5b5d9f4e87c5601c23b9bd37&stat=instructions:u
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Feb 23, 2025
We cannot infer more information from backedges in
`solveBlockValueNonLocal`. However, since DT is unavailable in LVI,
there is not a precise way to check whether a BB edge is a backedge.
This patch only skips self loops to unblock the range analysis.

The motivating case is extracted from
llvm/llvm-project#127663.

Compile-time impact is high:
https://llvm-compile-time-tracker.com/compare.php?from=84ddda58c870681dd12ed765e9d59d5e00567f94&to=af032f1351358f2f5b5d9f4e87c5601c23b9bd37&stat=instructions:u
@citymarina citymarina deleted the ce-ne-zero-2 branch May 12, 2025 16:07
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.

4 participants