[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