[llvm-branch-commits] [llvm] [LoopInterchange] Bail out if inner latch branch cond is not CmpInst (PR #202726)
Ryotaro Kasuga via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jun 9 10:53:05 PDT 2026
https://github.com/kasuga-fj created https://github.com/llvm/llvm-project/pull/202726
None
>From 5a62b2a1f12ed32029d6d4a7f3d41909f85f4ef6 Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Wed, 10 Jun 2026 02:51:15 +0900
Subject: [PATCH] [LoopInterchange] Bail out if inner latch branch cond is not
CmpInst
---
.../lib/Transforms/Scalar/LoopInterchange.cpp | 98 ++++++++++---------
.../complex-inner-latch-condition.ll | 31 ++----
2 files changed, 58 insertions(+), 71 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index cb1327b75260f..f0cf9d6d4724f 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -923,60 +923,62 @@ bool LoopInterchangeLegality::isLoopStructureUnderstood() {
dyn_cast<CondBrInst>(InnerLoopLatch->getTerminator());
if (!InnerLoopLatchBI)
return false;
- if (CmpInst *InnerLoopCmp =
- dyn_cast<CmpInst>(InnerLoopLatchBI->getCondition())) {
- Value *Op0 = InnerLoopCmp->getOperand(0);
- Value *Op1 = InnerLoopCmp->getOperand(1);
-
- // LHS and RHS of the inner loop exit condition, e.g.,
- // in "for(int j=0;j<i;j++)", LHS is j and RHS is i.
- Value *Left = nullptr;
- Value *Right = nullptr;
-
- // Check if V only involves inner loop induction variable.
- // Return true if V is InnerInduction, or a cast from
- // InnerInduction, or a binary operator that involves
- // InnerInduction and a constant.
- std::function<bool(Value *)> IsPathToInnerIndVar;
- IsPathToInnerIndVar = [this, &IsPathToInnerIndVar](const Value *V) -> bool {
- if (llvm::is_contained(InnerLoopInductions, V))
- return true;
- if (isa<Constant>(V))
- return true;
- const Instruction *I = dyn_cast<Instruction>(V);
- if (!I)
- return false;
- if (isa<CastInst>(I))
- return IsPathToInnerIndVar(I->getOperand(0));
- if (isa<BinaryOperator>(I))
- return IsPathToInnerIndVar(I->getOperand(0)) &&
- IsPathToInnerIndVar(I->getOperand(1));
- return false;
- };
-
- // In case of multiple inner loop indvars, it is okay if LHS and RHS
- // are both inner indvar related variables.
- if (IsPathToInnerIndVar(Op0) && IsPathToInnerIndVar(Op1))
- return true;
- // Otherwise we check if the cmp instruction compares an inner indvar
- // related variable (Left) with a outer loop invariant (Right).
- if (IsPathToInnerIndVar(Op0) && !isa<Constant>(Op0)) {
- Left = Op0;
- Right = Op1;
- } else if (IsPathToInnerIndVar(Op1) && !isa<Constant>(Op1)) {
- Left = Op1;
- Right = Op0;
- }
+ CmpInst *InnerLoopCmp = dyn_cast<CmpInst>(InnerLoopLatchBI->getCondition());
+ if (!InnerLoopCmp)
+ return false;
- if (Left == nullptr)
+ Value *Op0 = InnerLoopCmp->getOperand(0);
+ Value *Op1 = InnerLoopCmp->getOperand(1);
+
+ // LHS and RHS of the inner loop exit condition, e.g.,
+ // in "for(int j=0;j<i;j++)", LHS is j and RHS is i.
+ Value *Left = nullptr;
+ Value *Right = nullptr;
+
+ // Check if V only involves inner loop induction variable.
+ // Return true if V is InnerInduction, or a cast from
+ // InnerInduction, or a binary operator that involves
+ // InnerInduction and a constant.
+ std::function<bool(Value *)> IsPathToInnerIndVar;
+ IsPathToInnerIndVar = [this, &IsPathToInnerIndVar](const Value *V) -> bool {
+ if (llvm::is_contained(InnerLoopInductions, V))
+ return true;
+ if (isa<Constant>(V))
+ return true;
+ const Instruction *I = dyn_cast<Instruction>(V);
+ if (!I)
return false;
+ if (isa<CastInst>(I))
+ return IsPathToInnerIndVar(I->getOperand(0));
+ if (isa<BinaryOperator>(I))
+ return IsPathToInnerIndVar(I->getOperand(0)) &&
+ IsPathToInnerIndVar(I->getOperand(1));
+ return false;
+ };
- const SCEV *S = SE->getSCEV(Right);
- if (!SE->isLoopInvariant(S, OuterLoop))
- return false;
+ // In case of multiple inner loop indvars, it is okay if LHS and RHS
+ // are both inner indvar related variables.
+ if (IsPathToInnerIndVar(Op0) && IsPathToInnerIndVar(Op1))
+ return true;
+
+ // Otherwise we check if the cmp instruction compares an inner indvar
+ // related variable (Left) with a outer loop invariant (Right).
+ if (IsPathToInnerIndVar(Op0) && !isa<Constant>(Op0)) {
+ Left = Op0;
+ Right = Op1;
+ } else if (IsPathToInnerIndVar(Op1) && !isa<Constant>(Op1)) {
+ Left = Op1;
+ Right = Op0;
}
+ if (Left == nullptr)
+ return false;
+
+ const SCEV *S = SE->getSCEV(Right);
+ if (!SE->isLoopInvariant(S, OuterLoop))
+ return false;
+
return true;
}
diff --git a/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll b/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll
index 1527893167db5..a4d74fa5971cd 100644
--- a/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll
+++ b/llvm/test/Transforms/LoopInterchange/complex-inner-latch-condition.ll
@@ -8,44 +8,29 @@
; We currently doesn't support interchanging triangular loops, so the above
; loops must not be interchanged.
;
-; FIXME: Currently loop-interchange is applied.
-;
define void @complex_inner_latch_cond(ptr %A) {
; CHECK-LABEL: define void @complex_inner_latch_cond(
; CHECK-SAME: ptr [[A:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: br label %[[FOR_J_PREHEADER:.*]]
-; CHECK: [[FOR_I_HEADER_PREHEADER:.*]]:
-; CHECK-NEXT: br label %[[FOR_I_HEADER:.*]]
-; CHECK: [[FOR_I_HEADER]]:
-; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ], [ 0, %[[FOR_I_HEADER_PREHEADER]] ]
-; CHECK-NEXT: br label %[[FOR_J_SPLIT1:.*]]
-; CHECK: [[FOR_J_PREHEADER]]:
+; CHECK-NEXT: [[FOR_J_PREHEADER:.*]]:
; CHECK-NEXT: br label %[[FOR_J:.*]]
; CHECK: [[FOR_J]]:
-; CHECK-NEXT: [[J:%.*]] = phi i64 [ [[TMP0:%.*]], %[[FOR_J_SPLIT:.*]] ], [ 0, %[[FOR_J_PREHEADER]] ]
-; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER]]
-; CHECK: [[FOR_J_SPLIT1]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[FOR_J_PREHEADER]] ], [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ]
+; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER:.*]]
+; CHECK: [[FOR_I_HEADER_PREHEADER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[FOR_J]] ], [ [[J_NEXT:%.*]], %[[FOR_I_HEADER_PREHEADER]] ]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr [5 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: [[J_NEXT]] = add i64 [[J]], 1
; CHECK-NEXT: [[C0:%.*]] = icmp slt i64 [[J_NEXT]], [[I]]
; CHECK-NEXT: [[C1:%.*]] = icmp slt i64 [[J_NEXT]], 100
; CHECK-NEXT: [[EC_J_NOT:%.*]] = and i1 [[C0]], [[C1]]
-; CHECK-NEXT: br label %[[FOR_I_LATCH]]
-; CHECK: [[FOR_J_SPLIT]]:
-; CHECK-NEXT: [[I_LCSSA:%.*]] = phi i64 [ [[I]], %[[FOR_I_LATCH]] ]
-; CHECK-NEXT: [[TMP0]] = add i64 [[J]], 1
-; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i64 [[TMP0]], 100
-; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i64 [[TMP0]], [[I_LCSSA]]
-; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[TMP1]]
-; CHECK-NEXT: br i1 [[TMP3]], label %[[FOR_J]], label %[[EXIT:.*]]
+; CHECK-NEXT: br i1 [[EC_J_NOT]], label %[[FOR_I_HEADER_PREHEADER]], label %[[FOR_I_LATCH]]
; CHECK: [[FOR_I_LATCH]]:
; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1
; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_NEXT]], 5
-; CHECK-NEXT: br i1 [[EC_I]], label %[[FOR_J_SPLIT]], label %[[FOR_I_HEADER]]
+; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[FOR_J]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret void
;
More information about the llvm-branch-commits
mailing list