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

Abhay Kanhere via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 19:27:37 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] [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
+}



More information about the llvm-commits mailing list