Skip to content

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Oct 5, 2025

This patch implements the todo discussed in #158495 (comment).
It also fixes a regression introduced by #161000. See also dtcxzyw/llvm-opt-benchmark#2890 (comment).

IR diff: dtcxzyw/llvm-opt-benchmark#2892

@llvmbot
Copy link
Member

llvmbot commented Oct 5, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-function-specialization

Author: Yingwei Zheng (dtcxzyw)

Changes

This patch implements the todo discussed in #158495 (comment).
It also fixes a regression introduced by #161000. See also dtcxzyw/llvm-opt-benchmark#2890 (comment).

IR diff: dtcxzyw/llvm-opt-benchmark#2892


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/SCCPSolver.cpp (+22-17)
  • (modified) llvm/test/Transforms/SCCP/relax-range-checks.ll (+24)
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index af216cd9214bf..2e8e5ba18d933 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -317,24 +317,29 @@ static Value *simplifyInstruction(SCCPSolver &Solver,
       // Early exit if we know nothing about X.
       if (LRange.isFullSet())
         return nullptr;
-      // We are allowed to refine the comparison to either true or false for out
-      // of range inputs. Here we refine the comparison to true, i.e. we relax
-      // the range check.
-      auto NewCR = CR->exactUnionWith(LRange.inverse());
-      // TODO: Check if we can narrow the range check to an equality test.
-      // E.g, for X in [0, 4), X - 3 u< 2 -> X == 3
-      if (!NewCR)
+      auto ConvertCRToICmp =
+          [&](const std::optional<ConstantRange> &NewCR) -> Value * {
+        ICmpInst::Predicate Pred;
+        APInt RHS;
+        // Check if we can represent NewCR as an icmp predicate.
+        if (NewCR && NewCR->getEquivalentICmp(Pred, RHS)) {
+          IRBuilder<NoFolder> Builder(&Inst);
+          Value *NewICmp =
+              Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), RHS));
+          InsertedValues.insert(NewICmp);
+          return NewICmp;
+        }
         return nullptr;
-      ICmpInst::Predicate Pred;
-      APInt RHS;
-      // Check if we can represent NewCR as an icmp predicate.
-      if (NewCR->getEquivalentICmp(Pred, RHS)) {
-        IRBuilder<NoFolder> Builder(&Inst);
-        Value *NewICmp =
-            Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), RHS));
-        InsertedValues.insert(NewICmp);
-        return NewICmp;
-      }
+      };
+      // We are allowed to refine the comparison to either true or false for out
+      // of range inputs.
+      // Here we refine the comparison to false, and check if we can narrow the
+      // range check to an equality test.
+      if (auto *V = ConvertCRToICmp(CR->exactIntersectWith(LRange)))
+        return V;
+      // Here we refine the comparison to true, i.e. we relax the range check.
+      if (auto *V = ConvertCRToICmp(CR->exactUnionWith(LRange.inverse())))
+        return V;
     }
   }
 
diff --git a/llvm/test/Transforms/SCCP/relax-range-checks.ll b/llvm/test/Transforms/SCCP/relax-range-checks.ll
index 90722f350aa9e..34e48136df37a 100644
--- a/llvm/test/Transforms/SCCP/relax-range-checks.ll
+++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll
@@ -89,4 +89,28 @@ define i1 @relax_range_check_multiuse(i8 range(i8 0, 5) %x)  {
   ret i1 %ret
 }
 
+define i1 @range_check_to_icmp_eq1(i32 range(i32 0, 4) %x) {
+; CHECK-LABEL: define i1 @range_check_to_icmp_eq1(
+; CHECK-SAME: i32 range(i32 0, 4) [[X:%.*]]) {
+; CHECK-NEXT:    [[OFF:%.*]] = add nsw i32 [[X]], -3
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[X]], 3
+; CHECK-NEXT:    ret i1 [[TMP1]]
+;
+  %off = add nsw i32 %x, -3
+  %cmp = icmp ult i32 %off, 2
+  ret i1 %cmp
+}
+
+define i1 @range_check_to_icmp_eq2(i32 range(i32 -1, 2) %x) {
+; CHECK-LABEL: define i1 @range_check_to_icmp_eq2(
+; CHECK-SAME: i32 range(i32 -1, 2) [[X:%.*]]) {
+; CHECK-NEXT:    [[OFF:%.*]] = add nsw i32 [[X]], -1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[X]], 1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %off = add nsw i32 %x, -1
+  %cmp = icmp ult i32 %off, -2
+  ret i1 %cmp
+}
+
 declare void @use(i8)

Copy link
Member

@dianqk dianqk left a comment

Choose a reason for hiding this comment

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

The diff sounds reasonable to me.

Copy link
Contributor

@andjo403 andjo403 left a comment

Choose a reason for hiding this comment

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

Looks good to me

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. This can be further extended to use a mix of true and false for the out of range part.

@dtcxzyw dtcxzyw enabled auto-merge (squash) October 6, 2025 06:42
@dtcxzyw dtcxzyw merged commit 5e92e7f into llvm:main Oct 6, 2025
9 checks passed
@dtcxzyw dtcxzyw deleted the perf/sccp-strengthen-icmp branch October 6, 2025 07:14
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 6, 2025
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.

5 participants