[llvm] 8e65884 - [ConstraintElim] Derive facts for negative-step post-decrement cmps. (#218437)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 03:48:50 PDT 2026


Author: Florian Hahn
Date: 2026-08-25T11:48:45+01:00
New Revision: 8e658840bf1b737631818025041db33f041a4319

URL: https://github.com/llvm/llvm-project/commit/8e658840bf1b737631818025041db33f041a4319
DIFF: https://github.com/llvm/llvm-project/commit/8e658840bf1b737631818025041db33f041a4319.diff

LOG: [ConstraintElim] Derive facts for negative-step post-decrement cmps. (#218437)

Update addInfoForInductions to also add facts for negative step
inductions when comparing a post-inc induction.

The negative step support currently requires a step of -1, so the only
difference is the predicate of the precondition: for post-inc compares,
we exit at PN == B + 1, so strict predicates (B < StartValue) is used.

Helps to simplify a number of branches end-to-end in real world
workloads:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1025.

Note that there is one case where we now fail to runtime-unroll, as the
removed branch pessimizes SCEV, but I think this is a pre-existing
issue, and the vast majority of changes are improvements.

Alive2 Proofs: https://alive2.llvm.org/ce/z/LbJYNA

PR: https://github.com/llvm/llvm-project/pull/218437

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
    llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 1cf5cc4a8ab5f..054ab3b45e108 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1141,8 +1141,8 @@ void State::addInfoForInductions(BasicBlock &BB) {
   DomTreeNode *DTN = DT.getNode(InLoopSucc);
 
   // If we looked through `PN + C`, only derive facts when that add is
-  // really the induction's post-increment.
-  if (IncStep && (*IncStep != *StepOffset || StepOffset->isNegative()))
+  // really the induction's post-increment or post-decrement.
+  if (IncStep && *IncStep != *StepOffset)
     return;
 
   MonotonicInfo Info = getMonotonicityInfo(*PN, Backedge);
@@ -1154,23 +1154,28 @@ void State::addInfoForInductions(BasicBlock &BB) {
       return;
 
     // AR may wrap.
-    // Add StartValue >= PN conditional on B <= StartValue which guarantees that
-    // the loop exits before wrapping with a step of -1.
+    // The loop exits once the compared value reaches B, that is at PN == B when
+    // comparing the phi, and at PN == B + 1 for a post-decrement. Use
+    // non-strict predicate for the former, and a strict one for the latter to
+    // ensure the loop exits before wrapping.
+    CmpInst::Predicate UPrecond =
+        IncStep ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE;
+    ConditionTy BBeforeStartUnsigned = {UPrecond, B, StartValue};
+    ConditionTy BBeforeStartSigned = {ICmpInst::getSignedPredicate(UPrecond), B,
+                                      StartValue};
+
+    // AR may wrap, so both facts are conditional on B being below StartValue.
+    // Add StartValue >= PN, which holds as the loop exits before wrapping.
     WorkList.push_back(FactOrCheck::getConditionFact(
-        DTN, CmpInst::ICMP_UGE, StartValue, PN,
-        ConditionTy(CmpInst::ICMP_ULE, B, StartValue)));
+        DTN, CmpInst::ICMP_UGE, StartValue, PN, BBeforeStartUnsigned));
     if (!(Info.Decreasing && Info.Signed))
       WorkList.push_back(FactOrCheck::getConditionFact(
-          DTN, CmpInst::ICMP_SGE, StartValue, PN,
-          ConditionTy(CmpInst::ICMP_SLE, B, StartValue)));
-    // Add PN > B conditional on B <= StartValue which guarantees that the loop
-    // exits when reaching B with a step of -1.
-    WorkList.push_back(FactOrCheck::getConditionFact(
-        DTN, CmpInst::ICMP_UGT, PN, B,
-        ConditionTy(CmpInst::ICMP_ULE, B, StartValue)));
-    WorkList.push_back(FactOrCheck::getConditionFact(
-        DTN, CmpInst::ICMP_SGT, PN, B,
-        ConditionTy(CmpInst::ICMP_SLE, B, StartValue)));
+          DTN, CmpInst::ICMP_SGE, StartValue, PN, BBeforeStartSigned));
+    // Add PN > B, which holds as the loop exits when reaching B.
+    WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_UGT, PN,
+                                                     B, BBeforeStartUnsigned));
+    WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_SGT, PN,
+                                                     B, BBeforeStartSigned));
     return;
   }
 

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 e5e4dab8fcec9..3d509158c523f 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
@@ -634,12 +634,9 @@ define void @header_postdec_negative_step_folds() {
 ; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV_NEXT]], 2
 ; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
-; CHECK-NEXT:    [[UGT:%.*]] = icmp ugt i8 [[IV]], 2
-; CHECK-NEXT:    call void @use(i1 [[UGT]])
-; CHECK-NEXT:    [[ULE:%.*]] = icmp ule i8 [[IV]], 5
-; CHECK-NEXT:    call void @use(i1 [[ULE]])
-; CHECK-NEXT:    [[SGT:%.*]] = icmp sgt i8 [[IV]], 2
-; CHECK-NEXT:    call void @use(i1 [[SGT]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    br label %[[LOOP]]
 ; CHECK:       [[EXIT]]:
@@ -678,12 +675,9 @@ define void @latch_postdec_negative_step_folds() {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 5, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[UGT:%.*]] = icmp ugt i8 [[IV]], 2
-; CHECK-NEXT:    call void @use(i1 [[UGT]])
-; CHECK-NEXT:    [[ULE:%.*]] = icmp ule i8 [[IV]], 5
-; CHECK-NEXT:    call void @use(i1 [[ULE]])
-; CHECK-NEXT:    [[SGT:%.*]] = icmp sgt i8 [[IV]], 2
-; CHECK-NEXT:    call void @use(i1 [[SGT]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    br label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
@@ -769,10 +763,8 @@ define void @header_postdec_negative_step_strict_precond(i8 %start, i8 %b) {
 ; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV_NEXT]], [[B]]
 ; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT]], label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
-; CHECK-NEXT:    [[UGT:%.*]] = icmp ugt i8 [[IV]], [[B]]
-; CHECK-NEXT:    call void @use(i1 [[UGT]])
-; CHECK-NEXT:    [[ULE:%.*]] = icmp ule i8 [[IV]], [[START]]
-; CHECK-NEXT:    call void @use(i1 [[ULE]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    br label %[[LOOP]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
@@ -864,10 +856,8 @@ define void @header_postdec_negative_step_signed_precond(i8 %start, i8 %b) {
 ; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV_NEXT]], [[B]]
 ; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT]], label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
-; CHECK-NEXT:    [[SGT:%.*]] = icmp sgt i8 [[IV]], [[B]]
-; CHECK-NEXT:    call void @use(i1 [[SGT]])
-; CHECK-NEXT:    [[SLE:%.*]] = icmp sle i8 [[IV]], [[START]]
-; CHECK-NEXT:    call void @use(i1 [[SLE]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[UGT:%.*]] = icmp ugt i8 [[IV]], [[B]]
 ; CHECK-NEXT:    call void @use(i1 [[UGT]])
 ; CHECK-NEXT:    br label %[[LOOP]]
@@ -900,6 +890,52 @@ exit:
   ret void
 }
 
+; The compared offset of -2 does not match the induction step of -1, so the
+; loop exits at %iv == B + 2.
+define void @postdec_incstep_ne_step_not_folded(i8 %start, i8 %b) {
+; CHECK-LABEL: define void @postdec_incstep_ne_step_not_folded(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[PRE:%.*]] = icmp ult i8 [[B]], [[START]]
+; CHECK-NEXT:    br i1 [[PRE]], label %[[LOOP_PH:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PH]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[LOOP_PH]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[IV_MINUS2:%.*]] = add i8 [[IV]], -2
+; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV_MINUS2]], [[B]]
+; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT]], label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[UGT:%.*]] = icmp ugt i8 [[IV]], [[B]]
+; CHECK-NEXT:    call void @use(i1 [[UGT]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], -1
+; CHECK-NEXT:    br label %[[LOOP]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp ult i8 %b, %start
+  br i1 %pre, label %loop.ph, label %exit
+
+loop.ph:
+  br label %loop
+
+loop:
+  %iv = phi i8 [ %start, %loop.ph ], [ %iv.next, %loop.latch ]
+  %iv.minus2 = add i8 %iv, -2
+  %ec = icmp eq i8 %iv.minus2, %b
+  br i1 %ec, label %exit, label %loop.latch
+
+loop.latch:
+  %ugt = icmp ugt i8 %iv, %b
+  call void @use(i1 %ugt)
+  %iv.next = add i8 %iv, -1
+  br label %loop
+
+exit:
+  ret void
+}
+
 define void @latch_postdec_redundant_header_guard(i32 %n) {
 ; CHECK-LABEL: define void @latch_postdec_redundant_header_guard(
 ; CHECK-SAME: i32 [[N:%.*]]) {
@@ -908,8 +944,7 @@ define void @latch_postdec_redundant_header_guard(i32 %n) {
 ; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP:.*]], label %[[EXIT:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[N]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[IV]], 0
-; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_LATCH]], label %[[EXIT]]
+; CHECK-NEXT:    br i1 true, label %[[LOOP_LATCH]], label %[[EXIT]]
 ; CHECK:       [[LOOP_LATCH]]:
 ; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], -1
 ; CHECK-NEXT:    call void @launch(i32 [[IV_NEXT]])
@@ -963,10 +998,9 @@ define i64 @latch_postdec_umin_clamp(ptr %s, i64 %n) {
 ; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT]], label %[[LOOP]]
 ; CHECK:       [[IF_FOUND]]:
 ; CHECK-NEXT:    [[IDX:%.*]] = sub i64 [[N]], [[IV]]
-; CHECK-NEXT:    [[CLAMPED:%.*]] = call i64 @llvm.umin.i64(i64 [[N]], i64 [[IDX]])
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RES:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[N]], %[[LOOP_LATCH]] ], [ [[CLAMPED]], %[[IF_FOUND]] ]
+; CHECK-NEXT:    [[RES:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[N]], %[[LOOP_LATCH]] ], [ [[IDX]], %[[IF_FOUND]] ]
 ; CHECK-NEXT:    ret i64 [[RES]]
 ;
 entry:


        


More information about the llvm-commits mailing list