[llvm] [ConstraintElimination] Support widened (zext/sext) induction variables (PR #223214)
Alina Sbirlea via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 23:37:51 PDT 2026
https://github.com/alinas created https://github.com/llvm/llvm-project/pull/223214
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.
>From 6594098f8fdbf264968c42e6f91a897c074eb9b1 Mon Sep 17 00:00:00 2001
From: Alina Sbirlea <asbirlea at google.com>
Date: Sun, 13 Sep 2026 03:59:37 +0000
Subject: [PATCH] [ConstraintElimination] Support widened (zext/sext) induction
variables
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.
---
.../Scalar/ConstraintElimination.cpp | 43 ++++++++++++-----
.../widened-induction.ll | 46 +++++++++++++++++++
2 files changed, 78 insertions(+), 11 deletions(-)
create mode 100644 llvm/test/Transforms/ConstraintElimination/widened-induction.ll
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
+}
More information about the llvm-commits
mailing list