[llvm] [LoopNestAnalysis] Change the definition of perfect loop nest (NFC) (PR #206077)
Ehsan Amiri via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 27 11:08:39 PDT 2026
https://github.com/amehsan updated https://github.com/llvm/llvm-project/pull/206077
>From 0bb75ed2240b55073e8482ce0fa8e8f5a6345663 Mon Sep 17 00:00:00 2001
From: Ehsan Amiri <e00408328 at ptlabb01.fields-bluezone.huawei.com>
Date: Tue, 16 Jun 2026 22:17:43 +0000
Subject: [PATCH 1/5] [LoopNestAnalysis] Change structural condition of perfect
loop nest
---
llvm/lib/Analysis/LoopNestAnalysis.cpp | 126 +------
.../LoopNestAnalysis/nests-with-lcssa.ll | 67 +++-
.../Analysis/LoopNestAnalysis/perfectnest.ll | 343 +++++++++++++++++-
llvm/unittests/Analysis/LoopNestTest.cpp | 23 +-
4 files changed, 430 insertions(+), 129 deletions(-)
diff --git a/llvm/lib/Analysis/LoopNestAnalysis.cpp b/llvm/lib/Analysis/LoopNestAnalysis.cpp
index 06b5e8b85d1b3..ae11de402d2f7 100644
--- a/llvm/lib/Analysis/LoopNestAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopNestAnalysis.cpp
@@ -27,7 +27,7 @@ static const char *VerboseDebug = DEBUG_TYPE "-verbose";
/// perfect nesting:
/// - the inner loop should be the outer loop's only child
/// - the outer loop header should 'flow' into the inner loop preheader
-/// or jump around the inner loop to the outer loop latch
+/// through at most empty blocks (no conditional branches allowed)
/// - if the inner loop latch exits the inner loop, it should 'flow' into
/// the outer loop latch.
/// Returns true if the loop structure satisfies the basic requirements and
@@ -66,21 +66,7 @@ static CmpInst *getOuterLoopLatchCmp(const Loop &OuterLoop) {
return OuterLoopLatchCmp;
}
-static CmpInst *getInnerLoopGuardCmp(const Loop &InnerLoop) {
- CondBrInst *InnerGuard = InnerLoop.getLoopGuardBranch();
- CmpInst *InnerLoopGuardCmp =
- (InnerGuard) ? dyn_cast<CmpInst>(InnerGuard->getCondition()) : nullptr;
-
- DEBUG_WITH_TYPE(
- VerboseDebug, if (InnerLoopGuardCmp) {
- dbgs() << "Inner loop guard compare instruction: " << *InnerLoopGuardCmp
- << "\n";
- });
- return InnerLoopGuardCmp;
-}
-
static bool checkSafeInstruction(const Instruction &I,
- const CmpInst *InnerLoopGuardCmp,
const CmpInst *OuterLoopLatchCmp,
std::optional<Loop::LoopBounds> OuterLoopLB) {
@@ -89,10 +75,10 @@ static bool checkSafeInstruction(const Instruction &I,
if (!IsAllowed)
return false;
// The only binary instruction allowed is the outer loop step instruction,
- // the only comparison instructions allowed are the inner loop guard
- // compare instruction and the outer loop latch compare instruction.
+ // and the only comparison instruction allowed is the outer loop latch
+ // compare instruction.
if ((isa<BinaryOperator>(I) && &I != &OuterLoopLB->getStepInst()) ||
- (isa<CmpInst>(I) && &I != OuterLoopLatchCmp && &I != InnerLoopGuardCmp)) {
+ (isa<CmpInst>(I) && &I != OuterLoopLatchCmp)) {
return false;
}
return true;
@@ -116,7 +102,7 @@ LoopNest::LoopNestEnum LoopNest::analyzeLoopNestForPerfectNest(
// Determine whether the loops structure satisfies the following requirements:
// - the inner loop should be the outer loop's only child
// - the outer loop header should 'flow' into the inner loop preheader
- // or jump around the inner loop to the outer loop latch
+ // through at most empty blocks
// - if the inner loop latch exits the inner loop, it should 'flow' into
// the outer loop latch.
if (!checkLoopsStructure(OuterLoop, InnerLoop, SE)) {
@@ -133,17 +119,15 @@ LoopNest::LoopNestEnum LoopNest::analyzeLoopNestForPerfectNest(
}
CmpInst *OuterLoopLatchCmp = getOuterLoopLatchCmp(OuterLoop);
- CmpInst *InnerLoopGuardCmp = getInnerLoopGuardCmp(InnerLoop);
// Determine whether instructions in a basic block are one of:
- // - the inner loop guard comparison
// - the outer loop latch comparison
// - the outer loop induction variable increment
// - a phi node, a cast or a branch
auto containsOnlySafeInstructions = [&](const BasicBlock &BB) {
return llvm::all_of(BB, [&](const Instruction &I) {
- bool IsSafeInstr = checkSafeInstruction(I, InnerLoopGuardCmp,
- OuterLoopLatchCmp, OuterLoopLB);
+ bool IsSafeInstr =
+ checkSafeInstruction(I, OuterLoopLatchCmp, OuterLoopLB);
if (IsSafeInstr) {
DEBUG_WITH_TYPE(VerboseDebug, {
dbgs() << "Instruction: " << I << "\nin basic block:" << BB
@@ -203,12 +187,10 @@ LoopNest::InstrVectorTy LoopNest::getInterveningInstructions(
auto OuterLoopLB = OuterLoop.getBounds(SE);
CmpInst *OuterLoopLatchCmp = getOuterLoopLatchCmp(OuterLoop);
- CmpInst *InnerLoopGuardCmp = getInnerLoopGuardCmp(InnerLoop);
auto GetUnsafeInstructions = [&](const BasicBlock &BB) {
for (const Instruction &I : BB) {
- if (!checkSafeInstruction(I, InnerLoopGuardCmp, OuterLoopLatchCmp,
- OuterLoopLB)) {
+ if (!checkSafeInstruction(I, OuterLoopLatchCmp, OuterLoopLB)) {
Instr.push_back(&I);
DEBUG_WITH_TYPE(VerboseDebug, {
dbgs() << "Instruction: " << I << "\nin basic block:" << BB
@@ -332,94 +314,22 @@ static bool checkLoopsStructure(const Loop &OuterLoop, const Loop &InnerLoop,
InnerLoop.getExitingBlock() != InnerLoopLatch || !InnerLoopExit)
return false;
- // Returns whether the block `ExitBlock` contains at least one LCSSA Phi node.
- auto ContainsLCSSAPhi = [](const BasicBlock &ExitBlock) {
- return any_of(ExitBlock.phis(), [](const PHINode &PN) {
- return PN.getNumIncomingValues() == 1;
- });
- };
-
- // Returns whether the block `BB` qualifies for being an extra Phi block. The
- // extra Phi block is the additional block inserted after the exit block of an
- // "guarded" inner loop which contains "only" Phi nodes corresponding to the
- // LCSSA Phi nodes in the exit block.
- auto IsExtraPhiBlock = [&](const BasicBlock &BB) {
- return &*BB.getFirstNonPHIIt() == BB.getTerminator() &&
- all_of(BB.phis(), [&](const PHINode &PN) {
- return all_of(PN.blocks(), [&](const BasicBlock *IncomingBlock) {
- return IncomingBlock == InnerLoopExit ||
- IncomingBlock == OuterLoopHeader;
- });
- });
- };
-
- const BasicBlock *ExtraPhiBlock = nullptr;
- // Ensure the only branch that may exist between the loops is the inner loop
- // guard.
+ // Ensure the outer loop header flows directly into the inner loop preheader
+ // through at most empty (unconditional) blocks. Loop-invariant guards that
+ // previously branched from the outer header to the latch are removed by
+ // SimpleLoopUnswitch before loop nest analysis consumers (interchange,
+ // fusion) run, so no conditional branch is expected here.
if (OuterLoopHeader != InnerLoopPreHeader) {
const BasicBlock &SingleSucc =
LoopNest::skipEmptyBlockUntil(OuterLoopHeader, InnerLoopPreHeader);
-
- // no conditional branch present
- if (&SingleSucc != InnerLoopPreHeader) {
- const CondBrInst *BI = dyn_cast<CondBrInst>(SingleSucc.getTerminator());
-
- if (!BI || BI != InnerLoop.getLoopGuardBranch())
- return false;
-
- bool InnerLoopExitContainsLCSSA = ContainsLCSSAPhi(*InnerLoopExit);
-
- // The successors of the inner loop guard should be the inner loop
- // preheader or the outer loop latch possibly through empty blocks.
- for (const BasicBlock *Succ : BI->successors()) {
- const BasicBlock *PotentialInnerPreHeader = Succ;
- const BasicBlock *PotentialOuterLatch = Succ;
-
- // Ensure the inner loop guard successor is empty before skipping
- // blocks.
- if (Succ->size() == 1) {
- PotentialInnerPreHeader =
- &LoopNest::skipEmptyBlockUntil(Succ, InnerLoopPreHeader);
- PotentialOuterLatch =
- &LoopNest::skipEmptyBlockUntil(Succ, OuterLoopLatch);
- }
-
- if (PotentialInnerPreHeader == InnerLoopPreHeader)
- continue;
- if (PotentialOuterLatch == OuterLoopLatch)
- continue;
-
- // If `InnerLoopExit` contains LCSSA Phi instructions, additional block
- // may be inserted before the `OuterLoopLatch` to which `BI` jumps. The
- // loops are still considered perfectly nested if the extra block only
- // contains Phi instructions from InnerLoopExit and OuterLoopHeader.
- if (InnerLoopExitContainsLCSSA && IsExtraPhiBlock(*Succ) &&
- Succ->getSingleSuccessor() == OuterLoopLatch) {
- // Points to the extra block so that we can reference it later in the
- // final check. We can also conclude that the inner loop is
- // guarded and there exists LCSSA Phi node in the exit block later if
- // we see a non-null `ExtraPhiBlock`.
- ExtraPhiBlock = Succ;
- continue;
- }
-
- DEBUG_WITH_TYPE(VerboseDebug, {
- dbgs() << "Inner loop guard successor " << Succ->getName()
- << " doesn't lead to inner loop preheader or "
- "outer loop latch.\n";
- });
- return false;
- }
- }
+ if (&SingleSucc != InnerLoopPreHeader)
+ return false;
}
- // Ensure the inner loop exit block lead to the outer loop latch possibly
+ // Ensure the inner loop exit block leads to the outer loop latch possibly
// through empty blocks.
- if ((!ExtraPhiBlock ||
- &LoopNest::skipEmptyBlockUntil(InnerLoop.getExitBlock(),
- ExtraPhiBlock) != ExtraPhiBlock) &&
- (&LoopNest::skipEmptyBlockUntil(InnerLoop.getExitBlock(),
- OuterLoopLatch) != OuterLoopLatch)) {
+ if (&LoopNest::skipEmptyBlockUntil(InnerLoop.getExitBlock(),
+ OuterLoopLatch) != OuterLoopLatch) {
DEBUG_WITH_TYPE(
VerboseDebug,
dbgs() << "Inner loop exit block " << *InnerLoopExit
diff --git a/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll b/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
index c5d555b8ff326..d9d2df127a4f5 100644
--- a/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
+++ b/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
@@ -10,7 +10,7 @@
define i32 @f(i32 %N, i32 %M) #0 {
; CHECK: IsPerfect=true, Depth=1, OutermostLoop: for.j, Loops: ( for.j )
-; CHECK: IsPerfect=true, Depth=2, OutermostLoop: for.i, Loops: ( for.i for.j )
+; CHECK: IsPerfect=false, Depth=2, OutermostLoop: for.i, Loops: ( for.i for.j )
entry:
%cmp4 = icmp slt i32 0, %N
br i1 %cmp4, label %for.i.ph, label %for.i.end
@@ -61,6 +61,66 @@ for.i.end: ; preds = %for.i.end_crit_edge
ret i32 %res.0.lcssa
}
+; Same as @f, but the inner loop has no guard: the outer loop header
+; branches unconditionally into the inner loop preheader instead of
+; testing whether M > 0 first. Unlike @f, the inner loop exit also leads
+; directly (single hop) into the outer loop latch, with no intervening
+; critical-edge/LCSSA block, so this nest is perfectly nested.
+; int f_no_inner_guard(int N, int M) {
+; int res = 0;
+; for (int i = 0; i < N; ++i) {
+; for (int j = 0; j < M; ++j) res += i * j;
+; }
+; return res;
+; }
+define i32 @f_no_inner_guard(i32 %N, i32 %M) #0 {
+; CHECK: IsPerfect=true, Depth=1, OutermostLoop: for.j, Loops: ( for.j )
+; CHECK: IsPerfect=true, Depth=2, OutermostLoop: for.i, Loops: ( for.i for.j )
+entry:
+ %cmp4 = icmp slt i32 0, %N
+ br i1 %cmp4, label %for.i.ph, label %for.i.end
+
+for.i.ph: ; preds = %entry
+ br label %for.i
+
+for.i: ; preds = %for.i.ph, %for.i.inc
+ %i.06 = phi i32 [ 0, %for.i.ph ], [ %inc5, %for.i.inc ]
+ %res.05 = phi i32 [ 0, %for.i.ph ], [ %res.1.lcssa, %for.i.inc ]
+ br label %for.j.ph
+
+for.j.ph: ; preds = %for.i
+ br label %for.j
+
+for.j: ; preds = %for.j.ph, %for.j.inc
+ %j.03 = phi i32 [ 0, %for.j.ph ], [ %inc, %for.j.inc ]
+ %res.12 = phi i32 [ %res.05, %for.j.ph ], [ %add, %for.j.inc ]
+ %mul = mul nsw i32 %i.06, %j.03
+ %add = add nsw i32 %res.12, %mul
+ br label %for.j.inc
+
+for.j.inc: ; preds = %for.j
+ %inc = add nsw i32 %j.03, 1
+ %cmp2 = icmp slt i32 %inc, %M
+ br i1 %cmp2, label %for.j, label %for.j.end
+
+for.j.end: ; preds = %for.j.inc
+ %res.1.lcssa = phi i32 [ %add, %for.j.inc ]
+ br label %for.i.inc
+
+for.i.inc: ; preds = %for.j.end
+ %inc5 = add nsw i32 %i.06, 1
+ %cmp = icmp slt i32 %inc5, %N
+ br i1 %cmp, label %for.i, label %for.i.end_crit_edge
+
+for.i.end_crit_edge: ; preds = %for.i.inc
+ %split7 = phi i32 [ %res.1.lcssa, %for.i.inc ]
+ br label %for.i.end
+
+for.i.end: ; preds = %for.i.end_crit_edge, %entry
+ %res.0.lcssa = phi i32 [ %split7, %for.i.end_crit_edge ], [ 0, %entry ]
+ ret i32 %res.0.lcssa
+}
+
; int g(int N, int M, int K) {
; int sum = 0, prod = 1;
; for (int i = 0; i < N; ++i) {
@@ -159,6 +219,7 @@ for.i.end: ; preds = %for.i.end_crit_edge
ret i32 %add16
}
+
; int h(int N, int M, int K) {
; int sum = 0;
; for (int i = 0; i < N; ++i) {
@@ -172,8 +233,8 @@ for.i.end: ; preds = %for.i.end_crit_edge
; }
define i32 @h(i32 %N, i32 %M, i32 %K) #0 {
; CHECK: IsPerfect=true, Depth=1, OutermostLoop: for.k, Loops: ( for.k )
-; CHECK: IsPerfect=true, Depth=2, OutermostLoop: for.j, Loops: ( for.j for.k )
-; CHECK: IsPerfect=true, Depth=3, OutermostLoop: for.i, Loops: ( for.i for.j for.k )
+; CHECK: IsPerfect=false, Depth=2, OutermostLoop: for.j, Loops: ( for.j for.k )
+; CHECK: IsPerfect=false, Depth=3, OutermostLoop: for.i, Loops: ( for.i for.j for.k )
entry:
%cmp8 = icmp slt i32 0, %N
br i1 %cmp8, label %for.i.ph, label %for.i.end
diff --git a/llvm/test/Analysis/LoopNestAnalysis/perfectnest.ll b/llvm/test/Analysis/LoopNestAnalysis/perfectnest.ll
index b2e33223a8167..ab63ad1fd9551 100644
--- a/llvm/test/Analysis/LoopNestAnalysis/perfectnest.ll
+++ b/llvm/test/Analysis/LoopNestAnalysis/perfectnest.ll
@@ -7,7 +7,7 @@
define void @perf_nest_2D_1(ptr %y, ptr %x, i64 signext %nx, i64 signext %ny) {
; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_2D_1_loop_j, Loops: ( perf_nest_2D_1_loop_j )
-; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_2D_1_loop_i, Loops: ( perf_nest_2D_1_loop_i perf_nest_2D_1_loop_j )
+; CHECK-LABEL: IsPerfect=false, Depth=2, OutermostLoop: perf_nest_2D_1_loop_i, Loops: ( perf_nest_2D_1_loop_i perf_nest_2D_1_loop_j )
entry:
br label %perf_nest_2D_1_loop_i
@@ -42,6 +42,44 @@ perf_nest_2D_1_loop_i_end:
ret void
}
+; Same as perf_nest_2D_1 but without the loop-invariant guard branch around
+; the inner loop, so the nest is perfect at all levels.
+define void @perf_nest_2D_1_noguard(ptr %y, ptr %x, i64 signext %nx, i64 signext %ny) {
+; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_2D_1_noguard_loop_j, Loops: ( perf_nest_2D_1_noguard_loop_j )
+; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_2D_1_noguard_loop_i, Loops: ( perf_nest_2D_1_noguard_loop_i perf_nest_2D_1_noguard_loop_j )
+entry:
+ br label %perf_nest_2D_1_noguard_loop_i
+
+perf_nest_2D_1_noguard_loop_i:
+ %i = phi i64 [ 0, %entry ], [ %inc13, %inc_i ]
+ br label %perf_nest_2D_1_noguard_loop_j
+
+perf_nest_2D_1_noguard_loop_j:
+ %j = phi i64 [ 0, %perf_nest_2D_1_noguard_loop_i ], [ %inc, %inc_j ]
+ %arrayidx = getelementptr inbounds ptr, ptr %x, i64 %j
+ %0 = load ptr, ptr %arrayidx, align 8
+ %arrayidx6 = getelementptr inbounds i32, ptr %0, i64 %j
+ %1 = load i32, ptr %arrayidx6, align 4
+ %arrayidx8 = getelementptr inbounds ptr, ptr %y, i64 %j
+ %2 = load ptr, ptr %arrayidx8, align 8
+ %arrayidx11 = getelementptr inbounds i32, ptr %2, i64 %i
+ store i32 %1, ptr %arrayidx11, align 4
+ br label %inc_j
+
+inc_j:
+ %inc = add nsw i64 %j, 1
+ %cmp2 = icmp slt i64 %inc, %ny
+ br i1 %cmp2, label %perf_nest_2D_1_noguard_loop_j, label %inc_i
+
+inc_i:
+ %inc13 = add nsw i64 %i, 1
+ %cmp = icmp slt i64 %inc13, %nx
+ br i1 %cmp, label %perf_nest_2D_1_noguard_loop_i, label %perf_nest_2D_1_noguard_loop_i_end
+
+perf_nest_2D_1_noguard_loop_i_end:
+ ret void
+}
+
; Test a perfect 2-dim loop nest of the form:
; for (i=0; i<100; ++i)
; for (j=0; j<100; ++j)
@@ -87,7 +125,7 @@ perf_nest_2D_2_loop_i_end:
define void @perf_nest_2D_3(ptr %y, ptr %x, i64 signext %nx, i64 signext %ny) {
; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_2D_3_loop_j, Loops: ( perf_nest_2D_3_loop_j )
-; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_2D_3_loop_i, Loops: ( perf_nest_2D_3_loop_i perf_nest_2D_3_loop_j )
+; CHECK-LABEL: IsPerfect=false, Depth=2, OutermostLoop: perf_nest_2D_3_loop_i, Loops: ( perf_nest_2D_3_loop_i perf_nest_2D_3_loop_j )
entry:
br label %perf_nest_2D_3_loop_i
@@ -134,6 +172,57 @@ perf_nest_2D_3_loop_i_end:
ret void
}
+; Same as perf_nest_2D_3 but without the loop-invariant guard branch around
+; the inner loop (the conditional branch in %singleSucc), so the nest is
+; perfect at all levels.
+define void @perf_nest_2D_3_noguard(ptr %y, ptr %x, i64 signext %nx, i64 signext %ny) {
+; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_2D_3_noguard_loop_j, Loops: ( perf_nest_2D_3_noguard_loop_j )
+; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_2D_3_noguard_loop_i, Loops: ( perf_nest_2D_3_noguard_loop_i perf_nest_2D_3_noguard_loop_j )
+entry:
+ br label %perf_nest_2D_3_noguard_loop_i
+
+perf_nest_2D_3_noguard_loop_i:
+ %i = phi i64 [ 0, %entry ], [ %inc13, %inc_i ]
+ br label %singleSucc
+
+singleSucc:
+ br label %preheader.j
+
+preheader.j:
+ br label %perf_nest_2D_3_noguard_loop_j
+
+perf_nest_2D_3_noguard_loop_j:
+ %j = phi i64 [ 0, %preheader.j ], [ %inc, %inc_j ]
+ %arrayidx = getelementptr inbounds ptr, ptr %x, i64 %j
+ %0 = load ptr, ptr %arrayidx, align 8
+ %arrayidx6 = getelementptr inbounds i32, ptr %0, i64 %j
+ %1 = load i32, ptr %arrayidx6, align 4
+ %arrayidx8 = getelementptr inbounds ptr, ptr %y, i64 %j
+ %2 = load ptr, ptr %arrayidx8, align 8
+ %arrayidx11 = getelementptr inbounds i32, ptr %2, i64 %i
+ store i32 %1, ptr %arrayidx11, align 4
+ br label %inc_j
+
+inc_j:
+ %inc = add nsw i64 %j, 1
+ %cmp2 = icmp slt i64 %inc, %ny
+ br i1 %cmp2, label %perf_nest_2D_3_noguard_loop_j, label %for.exit
+
+for.exit:
+ br label %for.end
+
+for.end:
+ br label %inc_i
+
+inc_i:
+ %inc13 = add nsw i64 %i, 1
+ %cmp = icmp slt i64 %inc13, %nx
+ br i1 %cmp, label %perf_nest_2D_3_noguard_loop_i, label %perf_nest_2D_3_noguard_loop_i_end
+
+perf_nest_2D_3_noguard_loop_i_end:
+ ret void
+}
+
; Test a perfect 3-dim loop nest of the form:
; for (i=0; i<nx; ++i)
; for (j=0; j<ny; ++j)
@@ -143,8 +232,8 @@ perf_nest_2D_3_loop_i_end:
define void @perf_nest_3D_1(ptr %y, ptr %x, i32 signext %nx, i32 signext %ny, i32 signext %nk) {
; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_3D_1_loop_k, Loops: ( perf_nest_3D_1_loop_k )
-; CHECK-NEXT: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_3D_1_loop_j, Loops: ( perf_nest_3D_1_loop_j perf_nest_3D_1_loop_k )
-; CHECK-NEXT: IsPerfect=true, Depth=3, OutermostLoop: perf_nest_3D_1_loop_i, Loops: ( perf_nest_3D_1_loop_i perf_nest_3D_1_loop_j perf_nest_3D_1_loop_k )
+; CHECK-NEXT: IsPerfect=false, Depth=2, OutermostLoop: perf_nest_3D_1_loop_j, Loops: ( perf_nest_3D_1_loop_j perf_nest_3D_1_loop_k )
+; CHECK-NEXT: IsPerfect=false, Depth=3, OutermostLoop: perf_nest_3D_1_loop_i, Loops: ( perf_nest_3D_1_loop_i perf_nest_3D_1_loop_j perf_nest_3D_1_loop_k )
entry:
br label %perf_nest_3D_1_loop_i
@@ -199,6 +288,64 @@ perf_nest_3D_1_loop_i_end:
ret void
}
+; Same as perf_nest_3D_1 but without the loop-invariant guard branches around
+; the inner loops, so the nest is perfect at all levels.
+define void @perf_nest_3D_1_noguard(ptr %y, ptr %x, i32 signext %nx, i32 signext %ny, i32 signext %nk) {
+; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_3D_1_noguard_loop_k, Loops: ( perf_nest_3D_1_noguard_loop_k )
+; CHECK-NEXT: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_3D_1_noguard_loop_j, Loops: ( perf_nest_3D_1_noguard_loop_j perf_nest_3D_1_noguard_loop_k )
+; CHECK-NEXT: IsPerfect=true, Depth=3, OutermostLoop: perf_nest_3D_1_noguard_loop_i, Loops: ( perf_nest_3D_1_noguard_loop_i perf_nest_3D_1_noguard_loop_j perf_nest_3D_1_noguard_loop_k )
+entry:
+ br label %perf_nest_3D_1_noguard_loop_i
+
+perf_nest_3D_1_noguard_loop_i:
+ %i = phi i32 [ 0, %entry ], [ %inci, %for.inci ]
+ br label %perf_nest_3D_1_noguard_loop_j
+
+perf_nest_3D_1_noguard_loop_j:
+ %j = phi i32 [ 0, %perf_nest_3D_1_noguard_loop_i ], [ %incj, %for.incj ]
+ br label %perf_nest_3D_1_noguard_loop_k
+
+perf_nest_3D_1_noguard_loop_k:
+ %k = phi i32 [ 0, %perf_nest_3D_1_noguard_loop_j ], [ %inck, %for.inck ]
+ %idxprom = sext i32 %i to i64
+ %arrayidx = getelementptr inbounds ptr, ptr %x, i64 %idxprom
+ %0 = load ptr, ptr %arrayidx, align 8
+ %idxprom7 = sext i32 %j to i64
+ %arrayidx8 = getelementptr inbounds ptr, ptr %0, i64 %idxprom7
+ %1 = load ptr, ptr %arrayidx8, align 8
+ %idxprom9 = sext i32 %k to i64
+ %arrayidx10 = getelementptr inbounds i32, ptr %1, i64 %idxprom9
+ %2 = load i32, ptr %arrayidx10, align 4
+ %idxprom11 = sext i32 %j to i64
+ %arrayidx12 = getelementptr inbounds ptr, ptr %y, i64 %idxprom11
+ %3 = load ptr, ptr %arrayidx12, align 8
+ %idxprom13 = sext i32 %j to i64
+ %arrayidx14 = getelementptr inbounds ptr, ptr %3, i64 %idxprom13
+ %4 = load ptr, ptr %arrayidx14, align 8
+ %idxprom15 = sext i32 %k to i64
+ %arrayidx16 = getelementptr inbounds i32, ptr %4, i64 %idxprom15
+ store i32 %2, ptr %arrayidx16, align 4
+ br label %for.inck
+
+for.inck:
+ %inck = add nsw i32 %k, 1
+ %cmp5 = icmp slt i32 %inck, %nk
+ br i1 %cmp5, label %perf_nest_3D_1_noguard_loop_k, label %for.incj
+
+for.incj:
+ %incj = add nsw i32 %j, 1
+ %cmp2 = icmp slt i32 %incj, %ny
+ br i1 %cmp2, label %perf_nest_3D_1_noguard_loop_j, label %for.inci
+
+for.inci:
+ %inci = add nsw i32 %i, 1
+ %cmp = icmp slt i32 %inci, %nx
+ br i1 %cmp, label %perf_nest_3D_1_noguard_loop_i, label %perf_nest_3D_1_noguard_loop_i_end
+
+perf_nest_3D_1_noguard_loop_i_end:
+ ret void
+}
+
; Test a perfect 3-dim loop nest of the form:
; for (i=0; i<100; ++i)
; for (j=0; j<100; ++j)
@@ -278,7 +425,7 @@ perf_nest_3D_2_loop_i_end:
define signext i32 @perf_nest_live_out(i32 signext %x, i32 signext %ni, i32 signext %nj) {
; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_live_out_loop_j, Loops: ( perf_nest_live_out_loop_j )
-; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_live_out_loop_i, Loops: ( perf_nest_live_out_loop_i perf_nest_live_out_loop_j )
+; CHECK-LABEL: IsPerfect=false, Depth=2, OutermostLoop: perf_nest_live_out_loop_i, Loops: ( perf_nest_live_out_loop_i perf_nest_live_out_loop_j )
entry:
%cmp4 = icmp slt i32 0, %ni
br i1 %cmp4, label %perf_nest_live_out_loop_i.lr.ph, label %for.end7
@@ -323,6 +470,54 @@ for.end7:
ret i32 %x.addr.0.lcssa
}
+; Same as perf_nest_live_out but without the loop-invariant guard branch
+; around the inner loop, so the nest is perfect at all levels.
+define signext i32 @perf_nest_live_out_noguard(i32 signext %x, i32 signext %ni, i32 signext %nj) {
+; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: perf_nest_live_out_noguard_loop_j, Loops: ( perf_nest_live_out_noguard_loop_j )
+; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: perf_nest_live_out_noguard_loop_i, Loops: ( perf_nest_live_out_noguard_loop_i perf_nest_live_out_noguard_loop_j )
+entry:
+ %cmp4 = icmp slt i32 0, %ni
+ br i1 %cmp4, label %perf_nest_live_out_noguard_loop_i.lr.ph, label %for.end7
+
+perf_nest_live_out_noguard_loop_i.lr.ph:
+ br label %perf_nest_live_out_noguard_loop_i
+
+perf_nest_live_out_noguard_loop_i:
+ %x.addr.06 = phi i32 [ %x, %perf_nest_live_out_noguard_loop_i.lr.ph ], [ %x.addr.1.lcssa, %for.inc5 ]
+ %i.05 = phi i32 [ 0, %perf_nest_live_out_noguard_loop_i.lr.ph ], [ %inc6, %for.inc5 ]
+ br label %perf_nest_live_out_noguard_loop_j.lr.ph
+
+perf_nest_live_out_noguard_loop_j.lr.ph:
+ br label %perf_nest_live_out_noguard_loop_j
+
+perf_nest_live_out_noguard_loop_j:
+ %x.addr.13 = phi i32 [ %x.addr.06, %perf_nest_live_out_noguard_loop_j.lr.ph ], [ %add4, %perf_nest_live_out_noguard_loop_j ]
+ %j.02 = phi i32 [ 0, %perf_nest_live_out_noguard_loop_j.lr.ph ], [ %inc, %perf_nest_live_out_noguard_loop_j ]
+ %add = add nsw i32 %i.05, %j.02
+ %add4 = add nsw i32 %x.addr.13, %add
+ %inc = add nsw i32 %j.02, 1
+ %cmp2 = icmp slt i32 %inc, %nj
+ br i1 %cmp2, label %perf_nest_live_out_noguard_loop_j, label %for.cond1.for.inc5_crit_edge
+
+for.cond1.for.inc5_crit_edge:
+ %split = phi i32 [ %add4, %perf_nest_live_out_noguard_loop_j ]
+ br label %for.inc5
+
+for.inc5:
+ %x.addr.1.lcssa = phi i32 [ %split, %for.cond1.for.inc5_crit_edge ]
+ %inc6 = add nsw i32 %i.05, 1
+ %cmp = icmp slt i32 %inc6, %ni
+ br i1 %cmp, label %perf_nest_live_out_noguard_loop_i, label %for.cond.for.end7_crit_edge
+
+for.cond.for.end7_crit_edge:
+ %split7 = phi i32 [ %x.addr.1.lcssa, %for.inc5 ]
+ br label %for.end7
+
+for.end7:
+ %x.addr.0.lcssa = phi i32 [ %split7, %for.cond.for.end7_crit_edge ], [ %x, %entry ]
+ ret i32 %x.addr.0.lcssa
+}
+
; Test a perfect loop nest of the form:
; for (int i = 0; i < nx; ++i)
; if (i < ny) { // guard branch for the j-loop
@@ -331,7 +526,7 @@ for.end7:
; }
define double @perf_nest_guard_branch(ptr %y, ptr %x, i32 signext %nx, i32 signext %ny) {
; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: test6Loop2, Loops: ( test6Loop2 )
-; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: test6Loop1, Loops: ( test6Loop1 test6Loop2 )
+; CHECK-LABEL: IsPerfect=false, Depth=2, OutermostLoop: test6Loop1, Loops: ( test6Loop1 test6Loop2 )
entry:
%cmp2 = icmp slt i32 0, %nx
br i1 %cmp2, label %test6Loop1.lr.ph, label %for.end13
@@ -395,6 +590,73 @@ for.end13: ; preds = %for.cond.for.end13_
ret double %conv
}
+; Same as perf_nest_guard_branch but without the loop-invariant guard branch
+; around the inner loop, so the nest is perfect at all levels.
+define double @perf_nest_guard_branch_noguard(ptr %y, ptr %x, i32 signext %nx, i32 signext %ny) {
+; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: test6Loop2, Loops: ( test6Loop2 )
+; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: test6Loop1, Loops: ( test6Loop1 test6Loop2 )
+entry:
+ %cmp2 = icmp slt i32 0, %nx
+ br i1 %cmp2, label %test6Loop1.lr.ph, label %for.end13
+
+test6Loop1.lr.ph: ; preds = %entry
+ br label %test6Loop1
+
+test6Loop1: ; preds = %test6Loop1.lr.ph, %for.inc11
+ %i.0 = phi i32 [ 0, %test6Loop1.lr.ph ], [ %inc12, %for.inc11 ]
+ br label %test6Loop2.lr.ph
+
+test6Loop2.lr.ph:
+ br label %test6Loop2
+
+test6Loop2: ; preds = %test6Loop2.lr.ph, %for.inc
+ %j.0 = phi i32 [ %i.0, %test6Loop2.lr.ph ], [ %inc, %for.inc ]
+ %idxprom = sext i32 %i.0 to i64
+ %arrayidx = getelementptr inbounds ptr, ptr %x, i64 %idxprom
+ %0 = load ptr, ptr %arrayidx, align 8
+ %idxprom5 = sext i32 %j.0 to i64
+ %arrayidx6 = getelementptr inbounds i32, ptr %0, i64 %idxprom5
+ %1 = load i32, ptr %arrayidx6, align 4
+ %add = add nsw i32 %1, %j.0
+ %idxprom7 = sext i32 %j.0 to i64
+ %arrayidx8 = getelementptr inbounds ptr, ptr %y, i64 %idxprom7
+ %2 = load ptr, ptr %arrayidx8, align 8
+ %idxprom9 = sext i32 %i.0 to i64
+ %arrayidx10 = getelementptr inbounds i32, ptr %2, i64 %idxprom9
+ store i32 %add, ptr %arrayidx10, align 4
+ br label %for.inc
+
+for.inc: ; preds = %test6Loop2
+ %inc = add nsw i32 %j.0, 1
+ %cmp3 = icmp slt i32 %inc, %ny
+ br i1 %cmp3, label %test6Loop2, label %for.cond2.for.end_crit_edge
+
+for.cond2.for.end_crit_edge: ; preds = %for.inc
+ br label %for.end
+
+for.end: ; preds = %for.cond2.for.end_crit_edge
+ br label %if.end
+
+if.end: ; preds = %for.end
+ br label %for.inc11
+
+for.inc11: ; preds = %if.end
+ %inc12 = add nsw i32 %i.0, 1
+ %cmp = icmp slt i32 %inc12, %nx
+ br i1 %cmp, label %test6Loop1, label %for.cond.for.end13_crit_edge
+
+for.cond.for.end13_crit_edge: ; preds = %for.inc11
+ br label %for.end13
+
+for.end13: ; preds = %for.cond.for.end13_crit_edge, %entry
+ %arrayidx14 = getelementptr inbounds ptr, ptr %y, i64 0
+ %3 = load ptr, ptr %arrayidx14, align 8
+ %arrayidx15 = getelementptr inbounds i32, ptr %3, i64 0
+ %4 = load i32, ptr %arrayidx15, align 4
+ %conv = sitofp i32 %4 to double
+ ret double %conv
+}
+
; Test a perfect loop nest of the form:
; for (int i = 0; i < nx; ++i)
; if (i < ny) { // guard branch for the j-loop
@@ -404,7 +666,7 @@ for.end13: ; preds = %for.cond.for.end13_
define double @test6(ptr %y, ptr %x, i32 signext %nx, i32 signext %ny) {
; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: test6Loop2, Loops: ( test6Loop2 )
-; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: test6Loop1, Loops: ( test6Loop1 test6Loop2 )
+; CHECK-LABEL: IsPerfect=false, Depth=2, OutermostLoop: test6Loop1, Loops: ( test6Loop1 test6Loop2 )
entry:
%cmp2 = icmp slt i32 0, %nx
br i1 %cmp2, label %test6Loop1.lr.ph, label %for.end13
@@ -467,3 +729,70 @@ for.end13: ; preds = %for.cond.for.end13_
%conv = sitofp i32 %4 to double
ret double %conv
}
+
+; Same as test6 but without the loop-invariant guard branch around the
+; inner loop, so the nest is perfect at all levels.
+define double @test6_noguard(ptr %y, ptr %x, i32 signext %nx, i32 signext %ny) {
+; CHECK-LABEL: IsPerfect=true, Depth=1, OutermostLoop: test6Loop2, Loops: ( test6Loop2 )
+; CHECK-LABEL: IsPerfect=true, Depth=2, OutermostLoop: test6Loop1, Loops: ( test6Loop1 test6Loop2 )
+entry:
+ %cmp2 = icmp slt i32 0, %nx
+ br i1 %cmp2, label %test6Loop1.lr.ph, label %for.end13
+
+test6Loop1.lr.ph: ; preds = %entry
+ br label %test6Loop1
+
+test6Loop1: ; preds = %test6Loop1.lr.ph, %for.inc11
+ %i.0 = phi i32 [ 0, %test6Loop1.lr.ph ], [ %inc12, %for.inc11 ]
+ br label %test6Loop2.lr.ph
+
+test6Loop2.lr.ph:
+ br label %test6Loop2
+
+test6Loop2: ; preds = %test6Loop2.lr.ph, %for.inc
+ %j.0 = phi i32 [ %i.0, %test6Loop2.lr.ph ], [ %inc, %for.inc ]
+ %idxprom = sext i32 %i.0 to i64
+ %arrayidx = getelementptr inbounds ptr, ptr %x, i64 %idxprom
+ %0 = load ptr, ptr %arrayidx, align 8
+ %idxprom5 = sext i32 %j.0 to i64
+ %arrayidx6 = getelementptr inbounds i32, ptr %0, i64 %idxprom5
+ %1 = load i32, ptr %arrayidx6, align 4
+ %add = add nsw i32 %1, %j.0
+ %idxprom7 = sext i32 %j.0 to i64
+ %arrayidx8 = getelementptr inbounds ptr, ptr %y, i64 %idxprom7
+ %2 = load ptr, ptr %arrayidx8, align 8
+ %idxprom9 = sext i32 %i.0 to i64
+ %arrayidx10 = getelementptr inbounds i32, ptr %2, i64 %idxprom9
+ store i32 %add, ptr %arrayidx10, align 4
+ br label %for.inc
+
+for.inc: ; preds = %test6Loop2
+ %inc = add nsw i32 %j.0, 1
+ %cmp3 = icmp slt i32 %inc, %ny
+ br i1 %cmp3, label %test6Loop2, label %for.cond2.for.end_crit_edge
+
+for.cond2.for.end_crit_edge: ; preds = %for.inc
+ br label %for.end
+
+for.end: ; preds = %for.cond2.for.end_crit_edge
+ br label %if.end
+
+if.end: ; preds = %for.end
+ br label %for.inc11
+
+for.inc11: ; preds = %if.end
+ %inc12 = add nsw i32 %i.0, 1
+ %cmp = icmp slt i32 %inc12, %nx
+ br i1 %cmp, label %test6Loop1, label %for.cond.for.end13_crit_edge
+
+for.cond.for.end13_crit_edge: ; preds = %for.inc11
+ br label %for.end13
+
+for.end13: ; preds = %for.cond.for.end13_crit_edge, %entry
+ %arrayidx14 = getelementptr inbounds ptr, ptr %y, i64 0
+ %3 = load ptr, ptr %arrayidx14, align 8
+ %arrayidx15 = getelementptr inbounds i32, ptr %3, i64 0
+ %4 = load i32, ptr %arrayidx15, align 4
+ %conv = sitofp i32 %4 to double
+ ret double %conv
+}
diff --git a/llvm/unittests/Analysis/LoopNestTest.cpp b/llvm/unittests/Analysis/LoopNestTest.cpp
index df0a43be54759..2ef76af6e6591 100644
--- a/llvm/unittests/Analysis/LoopNestTest.cpp
+++ b/llvm/unittests/Analysis/LoopNestTest.cpp
@@ -57,8 +57,7 @@ TEST(LoopNestTest, PerfectLoopNest) {
" br label %for.outer\n"
"for.outer:\n"
" %i = phi i64 [ 0, %entry ], [ %inc13, %for.outer.latch ]\n"
- " %cmp21 = icmp slt i64 0, %ny\n"
- " br i1 %cmp21, label %for.inner.preheader, label %for.outer.latch\n"
+ " br label %for.inner.preheader\n"
"for.inner.preheader:\n"
" br label %for.inner\n"
"for.inner:\n"
@@ -205,15 +204,19 @@ TEST(LoopNestTest, ImperfectLoopNest) {
const ArrayRef<Loop*> Loops = LN.getLoops();
EXPECT_EQ(Loops.size(), 3ull);
- // Ensure the loop nest is recognized as having 2 separate perfect loops groups.
+ // With guard-handling removed from LoopNestAnalysis, the guarded outer
+ // loop header (loop.i branches to loop.j.preheader or for.inci via an
+ // invariant condition) is no longer treated as a perfect nest boundary.
+ // Each loop is its own singleton group.
const SmallVector<LoopVectorTy, 4> &PLV = LN.getPerfectLoops(SE);
- EXPECT_EQ(PLV.size(), 2ull);
- EXPECT_EQ(PLV.front().size(), 2ull);
- EXPECT_EQ(PLV.back().size(), 1ull);
+ EXPECT_EQ(PLV.size(), 3ull);
+ EXPECT_EQ(PLV[0].size(), 1ull);
+ EXPECT_EQ(PLV[1].size(), 1ull);
+ EXPECT_EQ(PLV[2].size(), 1ull);
// Ensure the nest depth and perfect nest depth are computed correctly.
EXPECT_EQ(LN.getNestDepth(), 3u);
- EXPECT_EQ(LN.getMaxPerfectDepth(), 2u);
+ EXPECT_EQ(LN.getMaxPerfectDepth(), 1u);
EXPECT_TRUE(LN.getInterveningInstructions(OL, *IL, SE).empty());
});
@@ -228,9 +231,8 @@ TEST(LoopNestTest, InterveningInstrLoopNest) {
" br label %for.outer\n"
"for.outer:\n"
" %i = phi i64 [ 0, %entry ], [ %inc13, %for.outer.latch ]\n"
- " %cmp21 = icmp slt i64 0, %ny\n"
" call void @outerheader()\n"
- " br i1 %cmp21, label %for.inner.preheader, label %for.outer.latch\n"
+ " br label %for.inner.preheader\n"
"for.inner.preheader:\n"
" %varr = getelementptr inbounds i32, ptr %A, i64 5\n"
" store i32 5, ptr %varr, align 4\n"
@@ -306,8 +308,7 @@ TEST(LoopNestTest, InterveningInstrLoopNest) {
Instruction *SI = getInstructionByName(F, "varr")->getNextNode();
Instruction *CI = SI->getNextNode();
- Instruction *OLH =
- getInstructionByName(F, "i")->getNextNode()->getNextNode();
+ Instruction *OLH = getInstructionByName(F, "i")->getNextNode();
Instruction *OLL = getInstructionByName(F, "inc13")->getNextNode();
Instruction *IE = getInstructionByName(F, "varr1")->getNextNode();
>From 1e0720f5314d96d38ef4e6e10684e9823d6127a0 Mon Sep 17 00:00:00 2001
From: Ehsan Amiri <ehsan.amiri at huawei.com>
Date: Fri, 26 Jun 2026 10:13:10 -0400
Subject: [PATCH 2/5] one more testcase
---
.../LoopNestAnalysis/nests-with-lcssa.ll | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll b/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
index d9d2df127a4f5..7831dd2447796 100644
--- a/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
+++ b/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
@@ -307,3 +307,76 @@ for.i.end: ; preds = %for.i.end_crit_edge
%sum.0.lcssa = phi i32 [ %split11, %for.i.end_crit_edge ], [ 0, %entry ]
ret i32 %sum.0.lcssa
}
+
+
+; Same as @h but without the loop-invariant guard branches inside the outer
+; loops (the "if (M > 0)" guard in for.i and "if (K > 0)" guard in for.j),
+; so the nest is perfectly nested at all levels.
+define i32 @h_perfect(i32 %N, i32 %M, i32 %K) #0 {
+; CHECK: IsPerfect=true, Depth=1, OutermostLoop: for.k, Loops: ( for.k )
+; CHECK: IsPerfect=true, Depth=2, OutermostLoop: for.j, Loops: ( for.j for.k )
+; CHECK: IsPerfect=true, Depth=3, OutermostLoop: for.i, Loops: ( for.i for.j for.k )
+entry:
+ %cmp8 = icmp slt i32 0, %N
+ br i1 %cmp8, label %for.i.ph, label %for.i.end
+
+for.i.ph: ; preds = %entry
+ br label %for.i
+
+for.i: ; preds = %for.i.ph, %for.i.inc
+ %i.010 = phi i32 [ 0, %for.i.ph ], [ %inc12, %for.i.inc ]
+ %sum.09 = phi i32 [ 0, %for.i.ph ], [ %sum.1.lcssa, %for.i.inc ]
+ br label %for.j.ph
+
+for.j.ph: ; preds = %for.i
+ br label %for.j
+
+for.j: ; preds = %for.j.ph, %for.j.inc
+ %j.06 = phi i32 [ 0, %for.j.ph ], [ %inc9, %for.j.inc ]
+ %sum.15 = phi i32 [ %sum.09, %for.j.ph ], [ %sum.2.lcssa, %for.j.inc ]
+ br label %for.k.ph
+
+for.k.ph: ; preds = %for.j
+ br label %for.k
+
+for.k: ; preds = %for.k.ph, %for.k.inc
+ %k.03 = phi i32 [ 0, %for.k.ph ], [ %inc, %for.k.inc ]
+ %sum.22 = phi i32 [ %sum.15, %for.k.ph ], [ %add, %for.k.inc ]
+ %mul = mul nsw i32 %i.010, %j.06
+ %mul7 = mul nsw i32 %mul, %k.03
+ %add = add nsw i32 %sum.22, %mul7
+ br label %for.k.inc
+
+for.k.inc: ; preds = %for.k
+ %inc = add nsw i32 %k.03, 1
+ %cmp5 = icmp slt i32 %inc, %K
+ br i1 %cmp5, label %for.k, label %for.k.end
+
+for.k.end: ; preds = %for.k.inc
+ %sum.2.lcssa = phi i32 [ %add, %for.k.inc ]
+ br label %for.j.inc
+
+for.j.inc: ; preds = %for.k.end
+ %inc9 = add nsw i32 %j.06, 1
+ %cmp2 = icmp slt i32 %inc9, %M
+ br i1 %cmp2, label %for.j, label %for.j.end
+
+for.j.end: ; preds = %for.j.inc
+ %sum.1.lcssa = phi i32 [ %sum.2.lcssa, %for.j.inc ]
+ br label %for.i.inc
+
+for.i.inc: ; preds = %for.j.end
+ %inc12 = add nsw i32 %i.010, 1
+ %cmp = icmp slt i32 %inc12, %N
+ br i1 %cmp, label %for.i, label %for.i.end_crit_edge
+
+for.i.end_crit_edge: ; preds = %for.i.inc
+ %split11 = phi i32 [ %sum.1.lcssa, %for.i.inc ]
+ br label %for.i.end
+
+for.i.end: ; preds = %for.i.end_crit_edge, %entry
+ %sum.0.lcssa = phi i32 [ %split11, %for.i.end_crit_edge ], [ 0, %entry ]
+ ret i32 %sum.0.lcssa
+}
+
+
>From 53285600770f83fd64ce17ffd63e8ffc78932471 Mon Sep 17 00:00:00 2001
From: Ehsan Amiri <ehsan.amiri at huawei.com>
Date: Fri, 26 Jun 2026 12:01:38 -0400
Subject: [PATCH 3/5] fix code-style
---
llvm/unittests/Analysis/LoopNestTest.cpp | 54 ++++++++++++------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/llvm/unittests/Analysis/LoopNestTest.cpp b/llvm/unittests/Analysis/LoopNestTest.cpp
index 2ef76af6e6591..8940edfac66a3 100644
--- a/llvm/unittests/Analysis/LoopNestTest.cpp
+++ b/llvm/unittests/Analysis/LoopNestTest.cpp
@@ -51,33 +51,33 @@ static Instruction *getInstructionByName(Function &F, StringRef Name) {
TEST(LoopNestTest, PerfectLoopNest) {
const char *ModuleStr =
- "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"
- "define void @foo(i64 signext %nx, i64 signext %ny) {\n"
- "entry:\n"
- " br label %for.outer\n"
- "for.outer:\n"
- " %i = phi i64 [ 0, %entry ], [ %inc13, %for.outer.latch ]\n"
- " br label %for.inner.preheader\n"
- "for.inner.preheader:\n"
- " br label %for.inner\n"
- "for.inner:\n"
- " %j = phi i64 [ 0, %for.inner.preheader ], [ %inc, %for.inner.latch ]\n"
- " br label %for.inner.latch\n"
- "for.inner.latch:\n"
- " %inc = add nsw i64 %j, 1\n"
- " %cmp2 = icmp slt i64 %inc, %ny\n"
- " br i1 %cmp2, label %for.inner, label %for.inner.exit\n"
- "for.inner.exit:\n"
- " br label %for.outer.latch\n"
- "for.outer.latch:\n"
- " %inc13 = add nsw i64 %i, 1\n"
- " %cmp = icmp slt i64 %inc13, %nx\n"
- " br i1 %cmp, label %for.outer, label %for.outer.exit\n"
- "for.outer.exit:\n"
- " br label %for.end\n"
- "for.end:\n"
- " ret void\n"
- "}\n";
+ "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"
+ "define void @foo(i64 signext %nx, i64 signext %ny) {\n"
+ "entry:\n"
+ " br label %for.outer\n"
+ "for.outer:\n"
+ " %i = phi i64 [ 0, %entry ], [ %inc13, %for.outer.latch ]\n"
+ " br label %for.inner.preheader\n"
+ "for.inner.preheader:\n"
+ " br label %for.inner\n"
+ "for.inner:\n"
+ " %j = phi i64 [ 0, %for.inner.preheader ], [ %inc, %for.inner.latch ]\n"
+ " br label %for.inner.latch\n"
+ "for.inner.latch:\n"
+ " %inc = add nsw i64 %j, 1\n"
+ " %cmp2 = icmp slt i64 %inc, %ny\n"
+ " br i1 %cmp2, label %for.inner, label %for.inner.exit\n"
+ "for.inner.exit:\n"
+ " br label %for.outer.latch\n"
+ "for.outer.latch:\n"
+ " %inc13 = add nsw i64 %i, 1\n"
+ " %cmp = icmp slt i64 %inc13, %nx\n"
+ " br i1 %cmp, label %for.outer, label %for.outer.exit\n"
+ "for.outer.exit:\n"
+ " br label %for.end\n"
+ "for.end:\n"
+ " ret void\n"
+ "}\n";
LLVMContext Context;
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);
>From 6b472ce87d3b2222ee18e6ead4f74a15968e2640 Mon Sep 17 00:00:00 2001
From: Ehsan Amiri <ehsan.amiri at huawei.com>
Date: Fri, 26 Jun 2026 13:48:44 -0400
Subject: [PATCH 4/5] change comment
---
llvm/lib/Analysis/LoopNestAnalysis.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/LoopNestAnalysis.cpp b/llvm/lib/Analysis/LoopNestAnalysis.cpp
index ae11de402d2f7..d57ca3a5119e9 100644
--- a/llvm/lib/Analysis/LoopNestAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopNestAnalysis.cpp
@@ -317,8 +317,7 @@ static bool checkLoopsStructure(const Loop &OuterLoop, const Loop &InnerLoop,
// Ensure the outer loop header flows directly into the inner loop preheader
// through at most empty (unconditional) blocks. Loop-invariant guards that
// previously branched from the outer header to the latch are removed by
- // SimpleLoopUnswitch before loop nest analysis consumers (interchange,
- // fusion) run, so no conditional branch is expected here.
+ // SimpleLoopUnswitch run, so no conditional branch is expected here.
if (OuterLoopHeader != InnerLoopPreHeader) {
const BasicBlock &SingleSucc =
LoopNest::skipEmptyBlockUntil(OuterLoopHeader, InnerLoopPreHeader);
>From f44065ad4e11cdad1ccc46df6b826685eeb0b492 Mon Sep 17 00:00:00 2001
From: Ehsan Amiri <ehsan.amiri at huawei.com>
Date: Sat, 27 Jun 2026 14:08:13 -0400
Subject: [PATCH 5/5] addressing comments
---
llvm/lib/Analysis/LoopNestAnalysis.cpp | 2 +-
llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/llvm/lib/Analysis/LoopNestAnalysis.cpp b/llvm/lib/Analysis/LoopNestAnalysis.cpp
index d57ca3a5119e9..36ef98563badf 100644
--- a/llvm/lib/Analysis/LoopNestAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopNestAnalysis.cpp
@@ -128,7 +128,7 @@ LoopNest::LoopNestEnum LoopNest::analyzeLoopNestForPerfectNest(
return llvm::all_of(BB, [&](const Instruction &I) {
bool IsSafeInstr =
checkSafeInstruction(I, OuterLoopLatchCmp, OuterLoopLB);
- if (IsSafeInstr) {
+ if (!IsSafeInstr) {
DEBUG_WITH_TYPE(VerboseDebug, {
dbgs() << "Instruction: " << I << "\nin basic block:" << BB
<< "is unsafe.\n";
diff --git a/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll b/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
index 7831dd2447796..a925dd077c82b 100644
--- a/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
+++ b/llvm/test/Analysis/LoopNestAnalysis/nests-with-lcssa.ll
@@ -219,7 +219,6 @@ for.i.end: ; preds = %for.i.end_crit_edge
ret i32 %add16
}
-
; int h(int N, int M, int K) {
; int sum = 0;
; for (int i = 0; i < N; ++i) {
@@ -308,7 +307,6 @@ for.i.end: ; preds = %for.i.end_crit_edge
ret i32 %sum.0.lcssa
}
-
; Same as @h but without the loop-invariant guard branches inside the outer
; loops (the "if (M > 0)" guard in for.i and "if (K > 0)" guard in for.j),
; so the nest is perfectly nested at all levels.
More information about the llvm-commits
mailing list