[llvm] [ConstraintElim] Derive signed facts for post-increment inductions. (PR #210079)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 08:17:14 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

<details>
<summary>Changes</summary>

Extend logic added in https://github.com/llvm/llvm-project/pull/209199
to also derive signed facts.

To do so, we keep track of whether LowerBound = Start + Step overflows
signed/unsigned. Code that previously bailed out if we were handling a
post-increment induction has been updated to use the computed
LowerBound, if it does not signed-wrap.

Alive2 Proof: https://alive2.llvm.org/ce/z/frskVt

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


3 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+18-14) 
- (modified) llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll (+54-6) 
- (modified) llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll (+1-2) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index f1f800cd69d60..676006a9e6201 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1072,38 +1072,42 @@ void State::addInfoForInductions(BasicBlock &BB) {
   }
 
   Value *LowerBound = StartValue;
+  bool UnsignedInBounds = true, SignedInBounds = true;
   if (IncStep) {
-    // Adjust lower bound when dealing with a post-increment value.
     auto *StartC = dyn_cast<ConstantInt>(StartValue);
     if (!StartC)
       return;
-    bool Overflow = false;
-    APInt Sum = StartC->getValue().uadd_ov(*StepOffset, Overflow);
-    if (Overflow)
-      return;
+    bool UOverflow = false, SOverflow = false;
+    APInt Sum = StartC->getValue().uadd_ov(*StepOffset, UOverflow);
+    (void)StartC->getValue().sadd_ov(*StepOffset, SOverflow);
     LowerBound = ConstantInt::get(StartValue->getType(), Sum);
+    UnsignedInBounds = !UOverflow;
+    SignedInBounds = !SOverflow;
   }
 
-  // AR may wrap. Add PN >= StartValue conditional on LowerBound <= B which
+  // AR may wrap. Add PN >= StartValue conditional on LowerBound <= B, which
   // guarantees that the loop exits before wrapping in combination with the
   // restrictions on B and the step above.
-  if (!MonotonicallyIncreasingUnsigned)
+  if (!MonotonicallyIncreasingUnsigned && UnsignedInBounds)
     WorkList.push_back(FactOrCheck::getConditionFact(
         DTN, CmpInst::ICMP_UGE, PN, StartValue,
         ConditionTy(CmpInst::ICMP_ULE, LowerBound, B)));
-  // Only unsigned facts are derived for the post-increment path.
-  if (!MonotonicallyIncreasingSigned && !IncStep)
+  if (!MonotonicallyIncreasingSigned && SignedInBounds)
     WorkList.push_back(FactOrCheck::getConditionFact(
         DTN, CmpInst::ICMP_SGE, PN, StartValue,
-        ConditionTy(CmpInst::ICMP_SLE, StartValue, B)));
+        ConditionTy(CmpInst::ICMP_SLE, LowerBound, B)));
+
+  if (SignedInBounds)
+    WorkList.push_back(FactOrCheck::getConditionFact(
+        DTN, CmpInst::ICMP_SLT, PN, B,
+        ConditionTy(CmpInst::ICMP_SLE, LowerBound, B)));
+
+  if (!UnsignedInBounds)
+    return;
 
   WorkList.push_back(FactOrCheck::getConditionFact(
       DTN, CmpInst::ICMP_ULT, PN, B,
       ConditionTy(CmpInst::ICMP_ULE, LowerBound, B)));
-  if (!IncStep)
-    WorkList.push_back(FactOrCheck::getConditionFact(
-        DTN, CmpInst::ICMP_SLT, PN, B,
-        ConditionTy(CmpInst::ICMP_SLE, StartValue, B)));
 
   // Try to add condition from header to the dedicated exit blocks. When exiting
   // either with EQ or NE in the header, we know that the induction value must
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
index 103fb73e4d37a..b1ac763592750 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
@@ -283,8 +283,8 @@ exit:
 }
 
 ; Post-increment condition with signed predicates in body.
-define i1 @postinc_negative_start_signed_body_not_folded(i1 %c) {
-; CHECK-LABEL: define i1 @postinc_negative_start_signed_body_not_folded(
+define i1 @postinc_negative_start_signed_body_folded(i1 %c) {
+; CHECK-LABEL: define i1 @postinc_negative_start_signed_body_folded(
 ; CHECK-SAME: i1 [[C:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*]]:
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
@@ -294,10 +294,9 @@ define i1 @postinc_negative_start_signed_body_not_folded(i1 %c) {
 ; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i64 [[IV_NEXT]], 10
 ; CHECK-NEXT:    br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
-; CHECK-NEXT:    [[RES:%.*]] = icmp slt i64 [[IV]], 10
 ; CHECK-NEXT:    br i1 [[C]], label %[[EXIT_0:.*]], label %[[LOOP_HEADER]]
 ; CHECK:       [[EXIT_0]]:
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -540,10 +539,9 @@ define void @postinc_signed_facts_when_unsigned_overflows(ptr %p, i1 %c) {
 ; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i8 [[IV_NEXT]], 100
 ; CHECK-NEXT:    br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
-; CHECK-NEXT:    [[UB:%.*]] = icmp slt i8 [[IV]], 100
 ; CHECK-NEXT:    [[UNS:%.*]] = icmp ult i8 [[IV]], 100
 ; CHECK-NEXT:    [[Z0:%.*]] = zext i1 false to i8
-; CHECK-NEXT:    [[Z1:%.*]] = zext i1 [[UB]] to i8
+; CHECK-NEXT:    [[Z1:%.*]] = zext i1 true to i8
 ; CHECK-NEXT:    [[Z2:%.*]] = zext i1 [[UNS]] to i8
 ; CHECK-NEXT:    store i8 [[Z0]], ptr [[P]], align 1
 ; CHECK-NEXT:    store i8 [[Z1]], ptr [[P]], align 1
@@ -576,3 +574,53 @@ loop.latch:
 exit:
   ret void
 }
+
+; Similar to @postinc_signed_facts_when_unsigned_overflows: here the signed sum
+; StartValue + Step (127 + 1) overflows signed but not unsigned.
+define void @postinc_unsigned_facts_when_signed_overflows(ptr %p) {
+; CHECK-LABEL: define void @postinc_unsigned_facts_when_signed_overflows(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 127, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[DONE:%.*]] = icmp eq i8 [[IV_NEXT]], -100
+; CHECK-NEXT:    br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[SLB:%.*]] = icmp slt i8 [[IV]], 127
+; CHECK-NEXT:    [[SUB:%.*]] = icmp slt i8 [[IV]], -100
+; CHECK-NEXT:    [[Z0:%.*]] = zext i1 [[SLB]] to i8
+; CHECK-NEXT:    [[Z1:%.*]] = zext i1 [[SUB]] to i8
+; CHECK-NEXT:    [[Z2:%.*]] = zext i1 true to i8
+; CHECK-NEXT:    store i8 [[Z0]], ptr [[P]], align 1
+; CHECK-NEXT:    store i8 [[Z1]], ptr [[P]], align 1
+; CHECK-NEXT:    store i8 [[Z2]], ptr [[P]], align 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 127, %entry ], [ %iv.next, %loop.latch ]
+  %iv.next = add i8 %iv, 1
+  %done = icmp eq i8 %iv.next, -100
+  br i1 %done, label %exit, label %loop.latch
+
+loop.latch:
+  %slb = icmp slt i8 %iv, 127
+  %sub = icmp slt i8 %iv, -100
+  %uns = icmp ult i8 %iv, -100
+  %z0 = zext i1 %slb to i8
+  %z1 = zext i1 %sub to i8
+  %z2 = zext i1 %uns to i8
+  store i8 %z0, ptr %p, align 1
+  store i8 %z1, ptr %p, align 1
+  store i8 %z2, ptr %p, align 1
+  br label %loop.header
+
+exit:
+  ret void
+}
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll
index 5aa179a25ccac..a5c0702896efd 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll
@@ -302,8 +302,7 @@ define void @signed_postinc_iv_step_1(i64 %end) {
 ; CHECK-NEXT:    [[CMP_I_NOT:%.*]] = icmp eq i64 [[IV_NEXT]], [[END]]
 ; CHECK-NEXT:    br i1 [[CMP_I_NOT]], label [[EXIT]], label [[LOOP_LATCH]]
 ; CHECK:       loop.latch:
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp slt i64 [[IV]], [[END]]
-; CHECK-NEXT:    call void @use(i1 [[CMP2]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    br label [[LOOP]]
 ; CHECK:       exit:

``````````

</details>


https://github.com/llvm/llvm-project/pull/210079


More information about the llvm-commits mailing list