[llvm] [ConstraintElimination] Support widened (zext/sext) induction variables (PR #223214)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 23:38:30 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Alina Sbirlea (alinas)
<details>
<summary>Changes</summary>
Extend State::addInfoForInductions() to match induction comparisons where the induction variable is widened via zext or sext (e.g. zext i32 %i to i64 != %bound). Record condition facts for the widened induction value (IndTarget) using the appropriately extended start value, allowing bounds checks against the widened value inside the loop body to be eliminated.
Added lit test in llvm/test/Transforms/ConstraintElimination/widened-induction.ll.
---
Full diff: https://github.com/llvm/llvm-project/pull/223214.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+32-11)
- (added) llvm/test/Transforms/ConstraintElimination/widened-induction.ll (+46)
``````````diff
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index ed95686e9e5d9..81ec059c49cfd 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1124,8 +1124,9 @@ void State::addInfoForInductions(BasicBlock &BB) {
PHINode *PN = nullptr;
const APInt *IncStep = nullptr;
CmpPredicate Pred;
- auto IndValue =
- m_Value(A, m_CombineOr(m_Phi(PN), m_IncrementOf(m_Phi(PN), IncStep)));
+ auto BaseInd = m_CombineOr(m_Phi(PN), m_IncrementOf(m_Phi(PN), IncStep));
+ auto IndValue = m_Value(
+ A, m_CombineOr(BaseInd, m_CombineOr(m_ZExt(BaseInd), m_SExt(BaseInd))));
auto *Br = dyn_cast<CondBrInst>(BB.getTerminator());
if (!Br)
@@ -1267,23 +1268,43 @@ void State::addInfoForInductions(BasicBlock &BB) {
// added precondition StartValue <= B for the former and the strict
// StartValue < B for the latter (which implies StartValue + Step <= B),
// neither PN nor the increment can wrap.
+ Value *IndTarget = PN;
+ Value *ExtStartValue = StartValue;
+ if (&BB == Header &&
+ (match(A, m_ZExt(m_Value())) || match(A, m_SExt(m_Value())))) {
+ IndTarget = A;
+ if (auto *StartC = dyn_cast<ConstantInt>(StartValue)) {
+ if (match(A, m_ZExt(m_Value()))) {
+ ExtStartValue = ConstantInt::get(
+ A->getType(),
+ StartC->getValue().zext(A->getType()->getScalarSizeInBits()));
+ } else {
+ ExtStartValue = ConstantInt::get(
+ A->getType(),
+ StartC->getValue().sext(A->getType()->getScalarSizeInBits()));
+ }
+ }
+ }
+
CmpInst::Predicate UPrecond = IncStep ? CmpInst::ICMP_ULT : CmpInst::ICMP_ULE;
- ConditionTy StartBeforeBoundUnsigned = {UPrecond, StartValue, B};
+ ConditionTy StartBeforeBoundUnsigned = {UPrecond, ExtStartValue, B};
ConditionTy StartBeforeBoundSigned = {ICmpInst::getSignedPredicate(UPrecond),
- StartValue, B};
+ ExtStartValue, B};
// Add PN >= StartValue, as the loop exits before wrapping.
if (!Info.Unsigned)
- WorkList.push_back(FactOrCheck::getConditionFact(
- DTN, CmpInst::ICMP_UGE, PN, StartValue, StartBeforeBoundUnsigned));
+ WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_UGE,
+ IndTarget, ExtStartValue,
+ StartBeforeBoundUnsigned));
if (!Info.Signed)
- WorkList.push_back(FactOrCheck::getConditionFact(
- DTN, CmpInst::ICMP_SGE, PN, StartValue, StartBeforeBoundSigned));
+ WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_SGE,
+ IndTarget, ExtStartValue,
+ StartBeforeBoundSigned));
// Add PN < B, as the loop exits once the compared value reaches B.
- WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_SLT, PN,
- B, StartBeforeBoundSigned));
WorkList.push_back(FactOrCheck::getConditionFact(
- DTN, CmpInst::ICMP_ULT, PN, B, StartBeforeBoundUnsigned));
+ DTN, CmpInst::ICMP_SLT, IndTarget, B, StartBeforeBoundSigned));
+ WorkList.push_back(FactOrCheck::getConditionFact(
+ DTN, CmpInst::ICMP_ULT, IndTarget, B, StartBeforeBoundUnsigned));
// Try to add condition from the header or latch to the dedicated exit
// blocks. When exiting either with EQ or NE, we know that the induction value
diff --git a/llvm/test/Transforms/ConstraintElimination/widened-induction.ll b/llvm/test/Transforms/ConstraintElimination/widened-induction.ll
new file mode 100644
index 0000000000000..2489584a2e7f1
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/widened-induction.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+declare void @use(i1)
+
+; Pattern where 32-bit induction variable is widened to 64-bit via zext in the header check.
+define void @test_zext_induction(i64 %vec_size) {
+; CHECK-LABEL: define void @test_zext_induction(
+; CHECK-SAME: i64 [[VEC_SIZE:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[I_ZEXT]], [[VEC_SIZE]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP_BODY:.*]], label %[[EXIT:.*]]
+; CHECK: [[LOOP_BODY]]:
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: br label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT: br label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %i.zext = zext i32 %i to i64
+ %cmp = icmp ne i64 %i.zext, %vec_size
+ br i1 %cmp, label %loop.body, label %exit
+
+loop.body:
+ %check = icmp ult i64 %i.zext, %vec_size
+ call void @use(i1 %check)
+ br label %loop.latch
+
+loop.latch:
+ %iv.next = add nuw nsw i32 %i, 1
+ br label %loop
+
+exit:
+ ret void
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/223214
More information about the llvm-commits
mailing list