[llvm] [ConstraintElim] decompose subtract with guard as precondition (PR #209615)

Abhay Kanhere via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 14:51:10 PDT 2026


https://github.com/AbhayKanhere updated https://github.com/llvm/llvm-project/pull/209615

>From c991c2441a6d1663f9a464774b801afe4914e9aa Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Tue, 14 Jul 2026 12:59:51 -0700
Subject: [PATCH 1/3] [ConstraintElim] decompose subtract with Unsigned<= guard
 as precondition
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

ConstraintElimination's unsigned constraint system already decomposes
    sub nuw a, b into a āˆ’ b (nuw guarantees b ≤ a),

Here we decompose 'sub nsw a, b' and general 'sub a,b' into  'a āˆ’ b'
        with precondition b u≤ a provided by dominating guard.

Motivating example is a bound check of the form
int f_signed(const int *a, int len, int k) {
  if (k <= len) {
    int off = len - k;      // sub nsw
    if (off <= len)         // redundant bounds check we want CE to fold
      return a[off];
  }
  return -1;
}

https://alive2.llvm.org/ce/z/e8vka7 sub nsw
https://alive2.llvm.org/ce/z/StmQDv sub without nsw nuw
---
 .../Scalar/ConstraintElimination.cpp          |  9 +++++++
 .../Transforms/ConstraintElimination/sub.ll   | 27 +++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index b244445c6e51f..5b3138e3c8b0b 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -664,6 +664,15 @@ static Decomposition decompose(Value *V,
     return V;
   }
 
+  if (match(V, m_Sub(m_Value(Op0), m_Value(Op1)))) {
+    Preconditions.emplace_back(CmpInst::ICMP_ULE, Op1, Op0);
+    auto ResA = decompose(Op0, Preconditions, IsSigned, DL);
+    auto ResB = decompose(Op1, Preconditions, IsSigned, DL);
+    if (!ResA.sub(ResB))
+      return ResA;
+    return V;
+  }
+
   return V;
 }
 
diff --git a/llvm/test/Transforms/ConstraintElimination/sub.ll b/llvm/test/Transforms/ConstraintElimination/sub.ll
index 78143dc62883c..0e63a8d7a0f50 100644
--- a/llvm/test/Transforms/ConstraintElimination/sub.ll
+++ b/llvm/test/Transforms/ConstraintElimination/sub.ll
@@ -239,3 +239,30 @@ if.end:                                           ; preds = %entry
 
 
 declare void @use(i1)
+
+; A `sub` without nsw/nuw is decomposed in the unsigned system under the
+; recorded precondition (Op1 u<= Op0). Given `%b u<= %a`, `%a - %b u<= %a` is
+; therefore known and the check folds to true.
+define i1 @sub_no_wrap_flags_ule(i8 %a, i8 %b) {
+; CHECK-LABEL: @sub_no_wrap_flags_ule(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[PRECOND:%.*]] = icmp ule i8 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    br i1 [[PRECOND]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK:       then:
+; CHECK-NEXT:    [[SUB:%.*]] = sub i8 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 true
+; CHECK:       else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %precond = icmp ule i8 %b, %a
+  br i1 %precond, label %then, label %else
+
+then:
+  %sub = sub i8 %a, %b
+  %c = icmp ule i8 %sub, %a
+  ret i1 %c
+
+else:
+  ret i1 false
+}

>From 6099e319eb5628945eea3f7ea829b9bb241521eb Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Thu, 16 Jul 2026 14:09:44 -0700
Subject: [PATCH 2/3] [test] update 
 Transforms/ConstraintElimination/reproducer-remarks.ll

Updated  ConstraintElimination/reproducer-remarks.ll
- added negative test for constant since now we fold GEP with constants
even if multiple uses are present.
- added positive test that checks earlier intended scenario
---
 .../reproducer-remarks.ll                     | 26 ++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll
index 63e1826ece5d7..81785e22bb322 100644
--- a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll
+++ b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks.ll
@@ -249,19 +249,39 @@ define <2 x i1> @vector_cmp(<2 x ptr> %vec) {
 }
 
 define i1 @shared_operand() {
-; CHECK-LABEL: define i1 @"{{.+}}shared_operandrepro"() {
+; Decomposing 'sub' under the 'b <=u a' precondition stops %sub being a shared
+; atom, so %sub.2 and %sub no longer cancel and the compare is not simplified.
+; CHECK-NOT: shared_operandrepro
+entry:
+  %sub = sub i8 0, 0
+  %sub.2 = sub nuw i8 %sub, 0
+  %c.5 = icmp ult i8 %sub.2, %sub
+  ret i1 %c.5
+}
+
+; Variable shared operand: with 'b <=u a' in scope the decomposed subs still
+; cancel, so the compare is retained (contrast @shared_operand above).
+define i1 @shared_operand_no_const(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @"{{.+}}shared_operand_no_constrepro"(i8 %a, i8 %b) {
 ; CHECK-NEXT: entry:
-; CHECK-NEXT:   %sub = sub i8 0, 0
+; CHECK-NEXT:   %0 = icmp ule i8 %b, %a
+; CHECK-NEXT:   call void @llvm.assume(i1 %0)
+; CHECK-NEXT:   %sub = sub i8 %a, %b
 ; CHECK-NEXT:   %sub.2 = sub nuw i8 %sub, 0
 ; CHECK-NEXT:   %c.5 = icmp ult i8 %sub.2, %sub
 ; CHECK-NEXT:   ret i1 %c.5
 ; CHECK-NEXT: }
 ;
 entry:
-  %sub = sub i8 0, 0
+  %precond = icmp ule i8 %b, %a
+  br i1 %precond, label %then, label %exit
+then:
+  %sub = sub i8 %a, %b
   %sub.2 = sub nuw i8 %sub, 0
   %c.5 = icmp ult i8 %sub.2, %sub
   ret i1 %c.5
+exit:
+  ret i1 false
 }
 
 @glob = external global i32

>From fafe0bb286b599981e17a7389b546bed9a92e1ab Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Thu, 16 Jul 2026 14:27:51 -0700
Subject: [PATCH 3/3] [ConstraintElim] review comment addressed

per review comment
merge the m_NUWSub match into m_Sub match with handling of NUW.
---
 llvm/lib/Transforms/Scalar/ConstraintElimination.cpp | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 5b3138e3c8b0b..e9ee6b1047645 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -656,16 +656,9 @@ static Decomposition decompose(Value *V,
     return V;
   }
 
-  if (match(V, m_NUWSub(m_Value(Op0), m_Value(Op1)))) {
-    auto ResA = decompose(Op0, Preconditions, IsSigned, DL);
-    auto ResB = decompose(Op1, Preconditions, IsSigned, DL);
-    if (!ResA.sub(ResB))
-      return ResA;
-    return V;
-  }
-
   if (match(V, m_Sub(m_Value(Op0), m_Value(Op1)))) {
-    Preconditions.emplace_back(CmpInst::ICMP_ULE, Op1, Op0);
+    if (!cast<OverflowingBinaryOperator>(V)->hasNoUnsignedWrap())
+      Preconditions.emplace_back(CmpInst::ICMP_ULE, Op1, Op0);
     auto ResA = decompose(Op0, Preconditions, IsSigned, DL);
     auto ResB = decompose(Op1, Preconditions, IsSigned, DL);
     if (!ResA.sub(ResB))



More information about the llvm-commits mailing list