[llvm-branch-commits] [llvm] [LoopInterchange] Drop ninf from instructions involved in interchange (PR #197923)
Ryotaro Kasuga via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri May 15 05:55:35 PDT 2026
https://github.com/kasuga-fj created https://github.com/llvm/llvm-project/pull/197923
None
>From 8b6731a2fe062a5b742dfd71956f671345c9a8eb Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Fri, 15 May 2026 12:53:52 +0000
Subject: [PATCH] [LoopInterchange] Drop ninf from instructions involved in
interchange
---
.../lib/Transforms/Scalar/LoopInterchange.cpp | 43 ++++++++++++++-----
llvm/test/Transforms/LoopInterchange/ninf.ll | 2 +-
.../LoopInterchange/reduction2mem.ll | 2 +-
3 files changed, 34 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 91e2510c33851..cd26b47b852ed 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -481,6 +481,8 @@ class LoopInterchangeLegality {
return HasNoWrapReductions;
}
+ ArrayRef<Instruction *> getHasNoInfInsts() const { return HasNoInfInsts; }
+
/// Record reductions in the inner loop. Currently supported reductions:
/// - initialized from a constant.
/// - reduction PHI node has only one user.
@@ -549,6 +551,10 @@ class LoopInterchangeLegality {
/// interchanging the loops.
SmallVector<Instruction *, 4> HasNoWrapReductions;
+ /// Hold instructions that have ninf flags and involved in reductions. Those
+ /// flags must be dropped when interchanging the loops.
+ SmallVector<Instruction *, 4> HasNoInfInsts;
+
/// Vector of reductions in the inner loop.
SmallVector<InnerReduction, 8> InnerReductions;
};
@@ -619,7 +625,8 @@ class LoopInterchangeTransform {
: OuterLoop(Outer), InnerLoop(Inner), SE(SE), LI(LI), DT(DT), LIL(LIL) {}
/// Interchange OuterLoop and InnerLoop.
- bool transform(ArrayRef<Instruction *> DropNoWrapInsts);
+ bool transform(ArrayRef<Instruction *> DropNoWrapInsts,
+ ArrayRef<Instruction *> DropNoInfInsts);
void reduction2Memory();
void restructureLoops(Loop *NewInner, Loop *NewOuter,
BasicBlock *OrigInnerPreHeader,
@@ -773,7 +780,7 @@ struct LoopInterchange {
});
LoopInterchangeTransform LIT(OuterLoop, InnerLoop, SE, LI, DT, LIL);
- LIT.transform(LIL.getHasNoWrapReductions());
+ LIT.transform(LIL.getHasNoWrapReductions(), LIL.getHasNoInfInsts());
LLVM_DEBUG(dbgs() << "Loops interchanged: outer loop '"
<< OuterLoop->getName() << "' and inner loop '"
<< InnerLoop->getName() << "'\n");
@@ -970,7 +977,8 @@ static Value *followLCSSA(Value *SV) {
}
static bool checkReductionKind(Loop *L, PHINode *PHI,
- SmallVectorImpl<Instruction *> &HasNoWrapInsts) {
+ SmallVectorImpl<Instruction *> &HasNoWrapInsts,
+ SmallVectorImpl<Instruction *> &HasNoInfInsts) {
RecurrenceDescriptor RD;
if (RecurrenceDescriptor::isReductionPHI(PHI, L, RD)) {
// Detect floating point reduction only when it can be reordered.
@@ -986,6 +994,13 @@ static bool checkReductionKind(Loop *L, PHINode *PHI,
case RecurKind::SMax:
case RecurKind::UMin:
case RecurKind::UMax:
+ case RecurKind::AnyOf:
+ return true;
+
+ // Change the order of floating-point operations may alter the results. If a
+ // certain instruction has ninf flag, it means that reordering can produce a
+ // poison value, which may lead to undefined behavior. To prevent this, we
+ // must drop the ninf flags if we decide to apply the transformation.
case RecurKind::FAdd:
case RecurKind::FMul:
case RecurKind::FMin:
@@ -995,7 +1010,9 @@ static bool checkReductionKind(Loop *L, PHINode *PHI,
case RecurKind::FMinimumNum:
case RecurKind::FMaximumNum:
case RecurKind::FMulAdd:
- case RecurKind::AnyOf:
+ for (Instruction *I : RD.getReductionOpChain(PHI, L))
+ if (isa<FPMathOperator>(I) && I->hasNoInfs())
+ HasNoInfInsts.push_back(I);
return true;
// Change the order of integer addition/multiplication may change the
@@ -1043,7 +1060,8 @@ static bool checkReductionKind(Loop *L, PHINode *PHI,
// Check V's users to see if it is involved in a reduction in L.
static PHINode *
findInnerReductionPhi(Loop *L, Value *V,
- SmallVectorImpl<Instruction *> &HasNoWrapInsts) {
+ SmallVectorImpl<Instruction *> &HasNoWrapInsts,
+ SmallVectorImpl<Instruction *> &HasNoInfInsts) {
// Reduction variables cannot be constants.
if (isa<Constant>(V))
return nullptr;
@@ -1053,7 +1071,7 @@ findInnerReductionPhi(Loop *L, Value *V,
if (PHI->getNumIncomingValues() == 1)
continue;
- if (checkReductionKind(L, PHI, HasNoWrapInsts))
+ if (checkReductionKind(L, PHI, HasNoWrapInsts, HasNoInfInsts))
return PHI;
else
return nullptr;
@@ -1110,7 +1128,7 @@ bool LoopInterchangeLegality::isInnerReduction(
return false;
// Check the reduction kind.
- if (!checkReductionKind(L, Phi, HasNoWrapInsts))
+ if (!checkReductionKind(L, Phi, HasNoWrapInsts, HasNoInfInsts))
return false;
// Find lcssa_phi in OuterLoop's Latch
@@ -1213,8 +1231,8 @@ bool LoopInterchangeLegality::findInductionAndReductions(
// Check if we have a PHI node in the outer loop that has a reduction
// result from the inner loop as an incoming value.
Value *V = followLCSSA(PHI.getIncomingValueForBlock(L->getLoopLatch()));
- PHINode *InnerRedPhi =
- findInnerReductionPhi(InnerLoop, V, HasNoWrapReductions);
+ PHINode *InnerRedPhi = findInnerReductionPhi(
+ InnerLoop, V, HasNoWrapReductions, HasNoInfInsts);
if (!InnerRedPhi ||
!llvm::is_contained(InnerRedPhi->incoming_values(), &PHI)) {
LLVM_DEBUG(
@@ -1926,7 +1944,8 @@ void LoopInterchangeTransform::reduction2Memory() {
}
bool LoopInterchangeTransform::transform(
- ArrayRef<Instruction *> DropNoWrapInsts) {
+ ArrayRef<Instruction *> DropNoWrapInsts,
+ ArrayRef<Instruction *> DropNoInfInsts) {
bool Transformed = false;
ArrayRef<LoopInterchangeLegality::InnerReduction> InnerReductions =
@@ -2032,12 +2051,14 @@ bool LoopInterchangeTransform::transform(
return false;
}
- // Finally, drop the nsw/nuw flags from the instructions for reduction
+ // Finally, drop the nsw/nuw/ninf flags from the instructions for reduction
// calculations.
for (Instruction *Reduction : DropNoWrapInsts) {
Reduction->setHasNoSignedWrap(false);
Reduction->setHasNoUnsignedWrap(false);
}
+ for (Instruction *I : DropNoInfInsts)
+ I->setHasNoInfs(false);
return true;
}
diff --git a/llvm/test/Transforms/LoopInterchange/ninf.ll b/llvm/test/Transforms/LoopInterchange/ninf.ll
index 23cd7610a5b30..da90d6d526fef 100644
--- a/llvm/test/Transforms/LoopInterchange/ninf.ll
+++ b/llvm/test/Transforms/LoopInterchange/ninf.ll
@@ -34,7 +34,7 @@ define noundef float @reduction_reassoc_ninf(ptr %A) {
; CHECK: [[FOR_J_SPLIT1]]:
; CHECK-NEXT: [[GEP:%.*]] = getelementptr [2 x float], ptr [[A]], i64 [[I]], i64 [[J]]
; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
-; CHECK-NEXT: [[SUM_J_NEXT]] = fadd reassoc ninf float [[SUM_J]], [[VAL]]
+; CHECK-NEXT: [[SUM_J_NEXT]] = fadd reassoc float [[SUM_J]], [[VAL]]
; CHECK-NEXT: [[J_INC:%.*]] = add i64 [[J]], 1
; CHECK-NEXT: [[EC_J:%.*]] = icmp eq i64 [[J_INC]], 2
; CHECK-NEXT: br label %[[FOR_I_LATCH]]
diff --git a/llvm/test/Transforms/LoopInterchange/reduction2mem.ll b/llvm/test/Transforms/LoopInterchange/reduction2mem.ll
index 1293c22b5a7a0..5618cb6c93e3f 100644
--- a/llvm/test/Transforms/LoopInterchange/reduction2mem.ll
+++ b/llvm/test/Transforms/LoopInterchange/reduction2mem.ll
@@ -37,7 +37,7 @@ define void @func(ptr noalias readonly %a, ptr noalias readonly %b, ptr noalias
; CHECK-NEXT: [[ADDR_B_J_I:%.*]] = getelementptr inbounds nuw [100 x double], ptr [[ADDR_B]], i64 [[INDEX_J]]
; CHECK-NEXT: [[B_J_I:%.*]] = load double, ptr [[ADDR_B_J_I]], align 8
; CHECK-NEXT: [[MUL:%.*]] = fmul fast double [[B_J_I]], [[A_J_I]]
-; CHECK-NEXT: [[ADD:%.*]] = fadd fast double [[MUL]], [[NEW_VAR]]
+; CHECK-NEXT: [[ADD:%.*]] = fadd reassoc nnan nsz arcp contract afn double [[MUL]], [[NEW_VAR]]
; CHECK-NEXT: store double [[ADD]], ptr [[ADDR_S]], align 8
; CHECK-NEXT: [[DEAD_J_NEXT:%.*]] = add nuw nsw i64 [[INDEX_J]], 1
; CHECK-NEXT: [[COND1:%.*]] = icmp eq i64 [[DEAD_J_NEXT]], 100
More information about the llvm-branch-commits
mailing list