[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