[llvm] [SCCP] Relax two-instruction range checks (PR #158495)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 14 08:17:37 PDT 2025


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/158495

If we know x in R1, the range check `x in R2` can be relaxed into `x in Union(R2, Inverse(R1))`.
Fixes regressions introduced by https://github.com/llvm/llvm-project/pull/156497.


>From 19c19ca0d54a2458c8560489ceef2ba4d102f2e0 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 14 Sep 2025 23:11:30 +0800
Subject: [PATCH 1/2] [SCCP] Add pre-commit tests. NFC.

---
 .../Transforms/SCCP/relax-range-checks.ll     | 50 +++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 llvm/test/Transforms/SCCP/relax-range-checks.ll

diff --git a/llvm/test/Transforms/SCCP/relax-range-checks.ll b/llvm/test/Transforms/SCCP/relax-range-checks.ll
new file mode 100644
index 0000000000000..ad9c476722174
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll
@@ -0,0 +1,50 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=sccp -S | FileCheck %s
+
+define i1 @relax_range_check(i8 range(i8 0, 5) %x)  {
+; CHECK-LABEL: define i1 @relax_range_check(
+; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) {
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X]], -3
+; CHECK-NEXT:    [[RET:%.*]] = icmp ult i8 [[ADD]], 2
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %add = add i8 %x, -3
+  %ret = icmp ult i8 %add, 2
+  ret i1 %ret
+}
+
+define i1 @relax_range_check_highbits_check(i8 range(i8 2, 0) %x)  {
+; CHECK-LABEL: define i1 @relax_range_check_highbits_check(
+; CHECK-SAME: i8 range(i8 2, 0) [[X:%.*]]) {
+; CHECK-NEXT:    [[AND:%.*]] = and i8 [[X]], -2
+; CHECK-NEXT:    [[RET:%.*]] = icmp eq i8 [[AND]], 2
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %and = and i8 %x, -2
+  %ret = icmp eq i8 %and, 2
+  ret i1 %ret
+}
+
+; Negative tests.
+
+define i1 @relax_range_check_one_instruction(i8 range(i8 0, 5) %x)  {
+; CHECK-LABEL: define i1 @relax_range_check_one_instruction(
+; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) {
+; CHECK-NEXT:    [[RET:%.*]] = icmp ult i8 [[X]], 2
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ret = icmp ult i8 %x, 2
+  ret i1 %ret
+}
+
+define i1 @relax_range_check_not_profitable(i8 range(i8 0, 6) %x)  {
+; CHECK-LABEL: define i1 @relax_range_check_not_profitable(
+; CHECK-SAME: i8 range(i8 0, 6) [[X:%.*]]) {
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X]], -3
+; CHECK-NEXT:    [[RET:%.*]] = icmp ult i8 [[ADD]], 2
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %add = add i8 %x, -3
+  %ret = icmp ult i8 %add, 2
+  ret i1 %ret
+}

>From 3a89d80c31011205af132a4090949ab2df09f3e1 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 14 Sep 2025 23:13:03 +0800
Subject: [PATCH 2/2] [SCCP] Relax two-instruction range checks.

---
 llvm/lib/Transforms/Utils/SCCPSolver.cpp      | 48 +++++++++++++++++++
 .../Transforms/SCCP/relax-range-checks.ll     |  4 +-
 2 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 84485176ad4ff..caaf069194b3b 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -19,8 +19,10 @@
 #include "llvm/Analysis/ValueLattice.h"
 #include "llvm/Analysis/ValueLatticeUtils.h"
 #include "llvm/Analysis/ValueTracking.h"
+#include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstVisitor.h"
+#include "llvm/IR/Instructions.h"
 #include "llvm/IR/NoFolder.h"
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/Support/Casting.h"
@@ -284,6 +286,52 @@ static Value *simplifyInstruction(SCCPSolver &Solver,
     return Sub;
   }
 
+  // Relax range checks.
+  if (auto *ICmp = dyn_cast<ICmpInst>(&Inst)) {
+    Value *X;
+    auto MatchTwoInstructionExactRangeCheck =
+        [&]() -> std::optional<ConstantRange> {
+      const APInt *RHSC;
+      if (!match(ICmp->getOperand(1), m_APInt(RHSC)))
+        return std::nullopt;
+
+      Value *LHS = ICmp->getOperand(0);
+      ICmpInst::Predicate Pred = ICmp->getPredicate();
+      unsigned BitWidth = RHSC->getBitWidth();
+      if (Pred == ICmpInst::ICMP_ULT) {
+        const APInt *Offset;
+        if (match(LHS, m_AddLike(m_Value(X), m_APInt(Offset))))
+          return ConstantRange(APInt::getZero(BitWidth), *RHSC).sub(*Offset);
+        return std::nullopt;
+      }
+      // Match icmp eq/ne X & NegPow2, C
+      if (ICmp->isEquality()) {
+        const APInt *Mask;
+        if (match(LHS, m_And(m_Value(X), m_NegatedPower2(Mask))) &&
+            RHSC->countr_zero() >= Mask->countr_zero()) {
+          ConstantRange CR(*RHSC, *RHSC - *Mask);
+          return Pred == ICmpInst::ICMP_EQ ? CR : CR.inverse();
+        }
+      }
+      return std::nullopt;
+    };
+
+    if (auto CR = MatchExactTwoInstructionRangeCheck()) {
+      ConstantRange LRange = GetRange(X);
+      if (auto NewCR = CR->exactUnionWith(LRange.inverse())) {
+        ICmpInst::Predicate Pred;
+        APInt RHS;
+        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;
+        }
+      }
+    }
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/SCCP/relax-range-checks.ll b/llvm/test/Transforms/SCCP/relax-range-checks.ll
index ad9c476722174..c5f90e419f710 100644
--- a/llvm/test/Transforms/SCCP/relax-range-checks.ll
+++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll
@@ -5,7 +5,7 @@ define i1 @relax_range_check(i8 range(i8 0, 5) %x)  {
 ; CHECK-LABEL: define i1 @relax_range_check(
 ; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) {
 ; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X]], -3
-; CHECK-NEXT:    [[RET:%.*]] = icmp ult i8 [[ADD]], 2
+; CHECK-NEXT:    [[RET:%.*]] = icmp uge i8 [[X]], 3
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %add = add i8 %x, -3
@@ -17,7 +17,7 @@ define i1 @relax_range_check_highbits_check(i8 range(i8 2, 0) %x)  {
 ; CHECK-LABEL: define i1 @relax_range_check_highbits_check(
 ; CHECK-SAME: i8 range(i8 2, 0) [[X:%.*]]) {
 ; CHECK-NEXT:    [[AND:%.*]] = and i8 [[X]], -2
-; CHECK-NEXT:    [[RET:%.*]] = icmp eq i8 [[AND]], 2
+; CHECK-NEXT:    [[RET:%.*]] = icmp ult i8 [[X]], 4
 ; CHECK-NEXT:    ret i1 [[RET]]
 ;
   %and = and i8 %x, -2



More information about the llvm-commits mailing list