[llvm] [ConstraintElim] Try to get induction step/start/flags from IR (NFC-ish) (PR #209284)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 14 05:22:34 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/209284
>From 6c6d35d9209844d965a1d60228b34bfeea73b9ec Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 9 Jul 2026 17:52:28 +0100
Subject: [PATCH] [ConstraintElim] Try to get induction step/start/flags from
IR (NFC-ish)
Handle the common integer inductions where the increment is plain add
PN, C by looking at IR instead of querying SCEV. There's effectively no
re-use between SCEV expression created in ConstraintElimination, and
handling simple cases in IR can avoid unnecessary, expensive SCEV
queries.
Should be NFC-ish. We miss trivial folding of the start value (see
regressed test), but that should not happen in end-to-end pipeline.
Improves compile-time for some workloads, ClamAV, SPASS:
https://llvm-compile-time-tracker.com/compare.php?from=cf41975d04e0a7f5bb0d62dbafa7da6e4c9e3a3&to=ad40c6f15d9cfced4a07c0c9e165b2e773c3c116&stat=instructions:u
I am working on follow-p changes that will handle more cases, where
avoiding unnecessary queries will be more important.
---
.../Scalar/ConstraintElimination.cpp | 84 ++++++++++---------
.../monotonic-int-phis-wrapping.ll | 3 +-
2 files changed, 47 insertions(+), 40 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 5c0377c454dd5..6136923d798ea 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -23,6 +23,7 @@
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/Analysis/ScalarEvolutionPatternMatch.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/DataLayout.h"
@@ -48,6 +49,7 @@
using namespace llvm;
using namespace PatternMatch;
+using namespace SCEVPatternMatch;
#define DEBUG_TYPE "constraint-elimination"
@@ -970,53 +972,46 @@ void State::addInfoForInductions(BasicBlock &BB) {
return;
BasicBlock *LoopPred = L->getLoopPredecessor();
- if (!LoopPred || !L->isLoopInvariant(B))
+ if (!LoopPred || !L->isLoopInvariant(B) || !SE.isSCEVable(PN->getType()))
return;
- auto *AR = dyn_cast_or_null<SCEVAddRecExpr>(SE.getSCEV(PN));
- if (!AR || AR->getLoop() != L)
- return;
-
- const SCEV *StartSCEV = AR->getStart();
- Value *StartValue = nullptr;
- if (auto *C = dyn_cast<SCEVConstant>(StartSCEV)) {
- StartValue = C->getValue();
+ Value *StartValue = PN->getIncomingValueForBlock(LoopPred);
+ BasicBlock *BackedgeBB = PN->getIncomingBlock(0) == LoopPred
+ ? PN->getIncomingBlock(1)
+ : PN->getIncomingBlock(0);
+ Value *Backedge = PN->getIncomingValueForBlock(BackedgeBB);
+ const APInt *StepOffset;
+ // Monotonicity is only used if the step is non-negative, so it reduces to the
+ // induction wrap flags.
+ bool MonotonicallyIncreasingUnsigned, MonotonicallyIncreasingSigned;
+ const SCEV *StartSCEV = nullptr;
+ if (match(Backedge, m_c_Add(m_Specific(PN), m_APInt(StepOffset)))) {
+ auto *Inc = cast<OverflowingBinaryOperator>(Backedge);
+ MonotonicallyIncreasingUnsigned = Inc->hasNoUnsignedWrap();
+ MonotonicallyIncreasingSigned = Inc->hasNoSignedWrap();
} else {
- StartValue = PN->getIncomingValueForBlock(LoopPred);
- assert(SE.getSCEV(StartValue) == StartSCEV && "inconsistent start value");
+ const SCEV *Expr = SE.getSCEV(PN);
+ if (!match(Expr,
+ m_scev_AffineAddRec(m_SCEV(StartSCEV), m_scev_APInt(StepOffset),
+ m_SpecificLoop(L))))
+ return;
+ MonotonicallyIncreasingUnsigned =
+ cast<SCEVAddRecExpr>(Expr)->hasNoUnsignedWrap();
+ MonotonicallyIncreasingSigned =
+ cast<SCEVAddRecExpr>(Expr)->hasNoSignedWrap();
}
DomTreeNode *DTN = DT.getNode(InLoopSucc);
- auto IncUnsigned = SE.getMonotonicPredicateType(AR, CmpInst::ICMP_UGT);
- auto IncSigned = SE.getMonotonicPredicateType(AR, CmpInst::ICMP_SGT);
- bool MonotonicallyIncreasingUnsigned =
- IncUnsigned == ScalarEvolution::MonotonicallyIncreasing;
- bool MonotonicallyIncreasingSigned =
- IncSigned == ScalarEvolution::MonotonicallyIncreasing;
- // If SCEV guarantees that AR does not wrap, PN >= StartValue can be added
- // unconditionally.
- if (MonotonicallyIncreasingUnsigned)
- WorkList.push_back(
- FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_UGE, PN, StartValue));
- if (MonotonicallyIncreasingSigned)
- WorkList.push_back(
- FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_SGE, PN, StartValue));
-
- APInt StepOffset;
- if (auto *C = dyn_cast<SCEVConstant>(AR->getStepRecurrence(SE)))
- StepOffset = C->getAPInt();
- else
- return;
// 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()))
+ if (IncStep && (*IncStep != *StepOffset || StepOffset->isNegative()))
return;
// Handle negative steps.
- if (StepOffset.isNegative()) {
+ if (StepOffset->isNegative()) {
// TODO: Extend to allow steps > -1.
- if (!(-StepOffset).isOne())
+ if (!(-*StepOffset).isOne())
return;
// AR may wrap.
@@ -1039,17 +1034,28 @@ void State::addInfoForInductions(BasicBlock &BB) {
return;
}
+ // If the induction is known not to wrap, PN >= StartValue can be added
+ // unconditionally.
+ if (MonotonicallyIncreasingUnsigned)
+ WorkList.push_back(
+ FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_UGE, PN, StartValue));
+ if (MonotonicallyIncreasingSigned)
+ WorkList.push_back(
+ FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_SGE, PN, StartValue));
+
// Make sure AR either steps by 1 or that the value we compare against is a
// GEP based on the same start value and all offsets are a multiple of the
// step size, to guarantee that the induction will reach the value.
- if (StepOffset.isZero() || StepOffset.isNegative())
+ if (StepOffset->isZero() || StepOffset->isNegative())
return;
- if (!StepOffset.isOne()) {
+ if (!StepOffset->isOne()) {
// Check whether B-Start is known to be a multiple of StepOffset.
+ if (!StartSCEV)
+ StartSCEV = SE.getSCEV(StartValue);
const SCEV *BMinusStart = SE.getMinusSCEV(SE.getSCEV(B), StartSCEV);
if (isa<SCEVCouldNotCompute>(BMinusStart) ||
- !SE.getConstantMultiple(BMinusStart).urem(StepOffset).isZero())
+ !SE.getConstantMultiple(BMinusStart).urem(*StepOffset).isZero())
return;
}
@@ -1060,7 +1066,7 @@ void State::addInfoForInductions(BasicBlock &BB) {
if (!StartC)
return;
bool Overflow = false;
- APInt Sum = StartC->getValue().uadd_ov(StepOffset, Overflow);
+ APInt Sum = StartC->getValue().uadd_ov(*StepOffset, Overflow);
if (Overflow)
return;
LowerBound = ConstantInt::get(StartValue->getType(), Sum);
@@ -1090,7 +1096,7 @@ void State::addInfoForInductions(BasicBlock &BB) {
// 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
// be u<= B, as other exits may only exit earlier.
- assert(!StepOffset.isNegative() && "induction must be increasing");
+ assert(!StepOffset->isNegative() && "induction must be increasing");
assert((Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE) &&
"unsupported predicate");
ConditionTy Precond = {CmpInst::ICMP_ULE, LowerBound, B};
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
index 17aa4d54abfba..ab77f54533626 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
@@ -96,7 +96,8 @@ define void @test_iv_nuw_nsw_2_uge_start(i8 %len.n, i8 %a) {
; CHECK-NEXT: [[C_2:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C_2]], label [[LOOP_LATCH]], label [[EXIT]]
; CHECK: loop.latch:
-; CHECK-NEXT: call void @use.i1(i1 true)
+; CHECK-NEXT: [[T_1:%.*]] = icmp uge i8 [[IV]], -1
+; CHECK-NEXT: call void @use.i1(i1 [[T_1]])
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
; CHECK-NEXT: br label [[LOOP_HEADER]]
; CHECK: exit:
More information about the llvm-commits
mailing list