[llvm-branch-commits] [llvm] [LoopInterchange] Reject if inner loop IV has outer-variant step (PR #202751)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Jun 9 12:51:54 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Ryotaro Kasuga (kasuga-fj)

<details>
<summary>Changes</summary>

In the legality check, there is a routine to detect and verify the induction variables of the inner loop. However, the validation was insufficient, specifically when the step values of the induction variables are not loop-invariant with respect to the outer loop.
This patch adds an additional check to ensure that the step values of those induction variables are also loop-invariant with respect to the outer loop.

Fixes #<!-- -->202383 and #<!-- -->202401.

---
Full diff: https://github.com/llvm/llvm-project/pull/202751.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/LoopInterchange.cpp (+17-8) 
- (modified) llvm/test/Transforms/LoopInterchange/inner-induciton-step-is-not-invariant.ll (+20-48) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index cb1327b75260f..adf65c81b678c 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -461,9 +461,12 @@ class LoopInterchangeLegality {
   bool canInterchangeLoops(unsigned InnerLoopId, unsigned OuterLoopId,
                            CharMatrix &DepMatrix);
 
-  /// Discover induction PHIs in the header of \p L. Induction
-  /// PHIs are added to \p Inductions.
-  bool findInductions(Loop *L, SmallVectorImpl<PHINode *> &Inductions);
+  /// Discover induction PHIs in the header of \p Inner. Induction PHIs are
+  /// added to \p Inductions. Return true if all induction PHIs in the header of
+  /// \p Inner have a step value which is invariant with respect to \p Outer,
+  /// and at least one such induction PHI is found.
+  bool findInductions(Loop *Outer, Loop *Inner,
+                      SmallVectorImpl<PHINode *> &Inductions);
 
   /// Check if the loop structure is understood. We do not handle triangular
   /// loops for now.
@@ -1370,11 +1373,17 @@ bool LoopInterchangeLegality::currentLimitations() {
 }
 
 bool LoopInterchangeLegality::findInductions(
-    Loop *L, SmallVectorImpl<PHINode *> &Inductions) {
-  for (PHINode &PHI : L->getHeader()->phis()) {
+    Loop *OuterLoop, Loop *InnerLoop, SmallVectorImpl<PHINode *> &Inductions) {
+  for (PHINode &PHI : InnerLoop->getHeader()->phis()) {
     InductionDescriptor ID;
-    if (InductionDescriptor::isInductionPHI(&PHI, L, SE, ID))
-      Inductions.push_back(&PHI);
+    if (!InductionDescriptor::isInductionPHI(&PHI, InnerLoop, SE, ID))
+      continue;
+    const SCEV *Step = ID.getStep();
+    if (!SE->isLoopInvariant(Step, OuterLoop)) {
+      Inductions.clear();
+      return false;
+    }
+    Inductions.push_back(&PHI);
   }
   return !Inductions.empty();
 }
@@ -1524,7 +1533,7 @@ bool LoopInterchangeLegality::canInterchangeLoops(unsigned InnerLoopId,
       return false;
     }
 
-  if (!findInductions(InnerLoop, InnerLoopInductions)) {
+  if (!findInductions(OuterLoop, InnerLoop, InnerLoopInductions)) {
     LLVM_DEBUG(dbgs() << "Could not find inner loop induction variables.\n");
     return false;
   }
diff --git a/llvm/test/Transforms/LoopInterchange/inner-induciton-step-is-not-invariant.ll b/llvm/test/Transforms/LoopInterchange/inner-induciton-step-is-not-invariant.ll
index 37bc9266942ed..f5960c7400cc8 100644
--- a/llvm/test/Transforms/LoopInterchange/inner-induciton-step-is-not-invariant.ll
+++ b/llvm/test/Transforms/LoopInterchange/inner-induciton-step-is-not-invariant.ll
@@ -10,43 +10,29 @@
 ;   for (j = 0, k = 0; k < 16 + i; j++, k += i)
 ;     A[8*j + i] += 1;
 ;
-; FIXME: This is now interchanged.
-;
 define void @step_of_k_is_i_0(ptr %A) {
 ; CHECK-LABEL: define void @step_of_k_is_i_0(
 ; CHECK-SAME: ptr [[A:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
-; CHECK:       [[OUTER_HEADER_PREHEADER:.*]]:
-; CHECK-NEXT:    br label %[[INNER:.*]]
-; CHECK:       [[INNER]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ], [ 1, %[[OUTER_HEADER_PREHEADER]] ]
-; CHECK-NEXT:    br label %[[INNER_SPLIT1:.*]]
-; CHECK:       [[OUTER_HEADER]]:
+; CHECK-NEXT:  [[OUTER_HEADER:.*]]:
 ; CHECK-NEXT:    br label %[[INNER1:.*]]
 ; CHECK:       [[INNER1]]:
-; CHECK-NEXT:    [[J:%.*]] = phi i64 [ [[TMP1:%.*]], %[[INNER_SPLIT:.*]] ], [ 0, %[[OUTER_HEADER]] ]
-; CHECK-NEXT:    [[K:%.*]] = phi i64 [ [[TMP0:%.*]], %[[INNER_SPLIT]] ], [ 0, %[[OUTER_HEADER]] ]
-; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER]]
-; CHECK:       [[INNER_SPLIT1]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 1, %[[OUTER_HEADER]] ], [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER:.*]]
+; CHECK:       [[OUTER_HEADER_PREHEADER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i64 [ 0, %[[INNER1]] ], [ [[J_NEXT:%.*]], %[[OUTER_HEADER_PREHEADER]] ]
+; CHECK-NEXT:    [[K:%.*]] = phi i64 [ 0, %[[INNER1]] ], [ [[K_NEXT:%.*]], %[[OUTER_HEADER_PREHEADER]] ]
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr [8 x i8], ptr [[A]], i64 [[J]], i64 [[I]]
 ; CHECK-NEXT:    [[OLD:%.*]] = load i8, ptr [[GEP]], align 1
 ; CHECK-NEXT:    [[NEW:%.*]] = add i8 [[OLD]], 1
 ; CHECK-NEXT:    store i8 [[NEW]], ptr [[GEP]], align 1
-; CHECK-NEXT:    [[J_NEXT:%.*]] = add i64 [[J]], 1
-; CHECK-NEXT:    [[K_NEXT:%.*]] = add i64 [[K]], [[I]]
-; CHECK-NEXT:    [[EC_INNER_NOT:%.*]] = icmp slt i64 [[K]], 16
-; CHECK-NEXT:    br label %[[OUTER_LATCH]]
-; CHECK:       [[INNER_SPLIT]]:
-; CHECK-NEXT:    [[I_LCSSA:%.*]] = phi i64 [ [[I]], %[[OUTER_LATCH]] ]
-; CHECK-NEXT:    [[TMP0]] = add i64 [[K]], [[I_LCSSA]]
-; CHECK-NEXT:    [[TMP1]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[J_NEXT]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[K_NEXT]] = add i64 [[K]], [[I]]
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp slt i64 [[K]], 16
-; CHECK-NEXT:    br i1 [[TMP2]], label %[[INNER1]], label %[[EXIT:.*]]
+; CHECK-NEXT:    br i1 [[TMP2]], label %[[OUTER_HEADER_PREHEADER]], label %[[OUTER_LATCH]]
 ; CHECK:       [[OUTER_LATCH]]:
 ; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
 ; CHECK-NEXT:    [[CMP_I:%.*]] = icmp slt i64 [[I_NEXT]], 8
-; CHECK-NEXT:    br i1 [[CMP_I]], label %[[INNER]], label %[[INNER_SPLIT]]
+; CHECK-NEXT:    br i1 [[CMP_I]], label %[[INNER1]], label %[[EXIT:.*]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
 ;
@@ -82,41 +68,27 @@ exit:
 ;   for (j = 0, k = 0; j < 30; j++, k += i)
 ;     A[i][j] = k;
 ;
-; FIXME: This is now interchanged.
-;
 define void @step_ok_k_is_i_1(ptr %A) {
 ; CHECK-LABEL: define void @step_ok_k_is_i_1(
 ; CHECK-SAME: ptr [[A:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
-; CHECK:       [[OUTER_HEADER_PREHEADER:.*]]:
-; CHECK-NEXT:    br label %[[INNER:.*]]
-; CHECK:       [[INNER]]:
-; CHECK-NEXT:    [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ], [ 0, %[[OUTER_HEADER_PREHEADER]] ]
-; CHECK-NEXT:    br label %[[INNER_SPLIT1:.*]]
-; CHECK:       [[OUTER_HEADER]]:
+; CHECK-NEXT:  [[OUTER_HEADER:.*]]:
 ; CHECK-NEXT:    br label %[[INNER1:.*]]
 ; CHECK:       [[INNER1]]:
-; CHECK-NEXT:    [[J:%.*]] = phi i64 [ [[TMP1:%.*]], %[[INNER_SPLIT:.*]] ], [ 0, %[[OUTER_HEADER]] ]
-; CHECK-NEXT:    [[K:%.*]] = phi i64 [ [[TMP0:%.*]], %[[INNER_SPLIT]] ], [ 0, %[[OUTER_HEADER]] ]
-; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER]]
-; CHECK:       [[INNER_SPLIT1]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER:.*]]
+; CHECK:       [[OUTER_HEADER_PREHEADER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i64 [ 0, %[[INNER1]] ], [ [[J_NEXT:%.*]], %[[OUTER_HEADER_PREHEADER]] ]
+; CHECK-NEXT:    [[K:%.*]] = phi i64 [ 0, %[[INNER1]] ], [ [[K_NEXT:%.*]], %[[OUTER_HEADER_PREHEADER]] ]
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds [40 x i64], ptr [[A]], i64 [[I]], i64 [[J]]
 ; CHECK-NEXT:    store i64 [[K]], ptr [[GEP]], align 4
-; CHECK-NEXT:    [[J_NEXT:%.*]] = add i64 [[J]], 1
-; CHECK-NEXT:    [[K_NEXT:%.*]] = add i64 [[K]], [[I]]
-; CHECK-NEXT:    [[EC_INNER:%.*]] = icmp eq i64 [[J]], 30
-; CHECK-NEXT:    br label %[[OUTER_LATCH]]
-; CHECK:       [[INNER_SPLIT]]:
-; CHECK-NEXT:    [[I_LCSSA:%.*]] = phi i64 [ [[I]], %[[OUTER_LATCH]] ]
-; CHECK-NEXT:    [[TMP0]] = add i64 [[K]], [[I_LCSSA]]
-; CHECK-NEXT:    [[TMP1]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[J_NEXT]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[K_NEXT]] = add i64 [[K]], [[I]]
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i64 [[J]], 30
-; CHECK-NEXT:    br i1 [[TMP2]], label %[[EXIT:.*]], label %[[INNER1]]
+; CHECK-NEXT:    br i1 [[TMP2]], label %[[OUTER_LATCH]], label %[[OUTER_HEADER_PREHEADER]]
 ; CHECK:       [[OUTER_LATCH]]:
 ; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
 ; CHECK-NEXT:    [[EC_I:%.*]] = icmp eq i64 [[I_NEXT]], 6
-; CHECK-NEXT:    br i1 [[EC_I]], label %[[INNER_SPLIT]], label %[[INNER]]
+; CHECK-NEXT:    br i1 [[EC_I]], label %[[EXIT:.*]], label %[[INNER1]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
 ;

``````````

</details>


https://github.com/llvm/llvm-project/pull/202751


More information about the llvm-branch-commits mailing list