[llvm] [LoopInterchange] Don't consider loops with BTC=0 (PR #167113)
Sjoerd Meijer via llvm-commits
llvm-commits at lists.llvm.org
Sat Nov 8 01:27:13 PST 2025
https://github.com/sjoerdmeijer updated https://github.com/llvm/llvm-project/pull/167113
>From c19c535d053c942498b0736f8ffccbdc98543b6f Mon Sep 17 00:00:00 2001
From: Sjoerd Meijer <smeijer at nvidia.com>
Date: Fri, 7 Nov 2025 06:23:51 -0800
Subject: [PATCH 1/2] [LoopInterchange] Don't consider loops with BTC=0
Do not consider loops with a zero backedge taken count as candidates for
interchange. This seems like a sensible thing to do to me, because it
suggests the loop doesn't execute and there is no point in
interchanging.
This avoids triggering an assert about phis and their uses. I have a
feeling that this fix might be hiding the issue, but I haven't yet been
able to trigger the assert with other test cases; every time the loops
are rejected for other reasons.
Since I think this is a self-contained improvement that avoids a lot
of test failures, I propose to reject this loops while I investigate
further if I can still trigger this in some way.
(Partial) fix for #163954
---
llvm/lib/Transforms/Scalar/LoopInterchange.cpp | 13 +++++++++++++
.../LoopInterchange/interchanged-loop-nest-4.ll | 2 +-
.../LoopInterchange/lcssa-phi-outer-latch.ll | 2 +-
.../LoopInterchange/pr43176-move-to-new-latch.ll | 2 +-
llvm/test/Transforms/LoopInterchange/pr43326.ll | 2 +-
llvm/test/Transforms/LoopInterchange/pr57148.ll | 2 +-
.../reductions-across-inner-and-outer-loop.ll | 2 +-
7 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 9aaf6a5aa4d6a..776560e678d8e 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -101,6 +101,12 @@ static cl::opt<unsigned int> MaxLoopNestDepth(
"loop-interchange-max-loop-nest-depth", cl::init(10), cl::Hidden,
cl::desc("Maximum depth of loop nest considered for the transform"));
+// This is mainly for testing purposes, and certain tests that rely on
+// behaviour that is more difficult to trigger otherwise.
+static cl::opt<bool> SkipLoopsWithZeroBTC(
+ "loop-interchange-skip-zero-btc", cl::init(true), cl::Hidden,
+ cl::desc("Do not consider loops with a backedge taken count of 0"));
+
// We prefer cache cost to vectorization by default.
static cl::list<RuleTy> Profitabilities(
"loop-interchange-profitabilities", cl::ZeroOrMore,
@@ -428,6 +434,13 @@ static bool isComputableLoopNest(ScalarEvolution *SE,
LLVM_DEBUG(dbgs() << "Couldn't compute backedge count\n");
return false;
}
+ // A loop with a backedge that isn't taken, e.g. an unconditional branch
+ // true, isn't really a loop and we don't want to consider it as a
+ // candidate.
+ if (ExitCountOuter && SkipLoopsWithZeroBTC && ExitCountOuter->isZero()) {
+ LLVM_DEBUG(dbgs() << "Single iteration loop\n");
+ return false;
+ }
if (L->getNumBackEdges() != 1) {
LLVM_DEBUG(dbgs() << "NumBackEdges is not equal to 1\n");
return false;
diff --git a/llvm/test/Transforms/LoopInterchange/interchanged-loop-nest-4.ll b/llvm/test/Transforms/LoopInterchange/interchanged-loop-nest-4.ll
index 70fff161154d8..a4dbd5a005082 100644
--- a/llvm/test/Transforms/LoopInterchange/interchanged-loop-nest-4.ll
+++ b/llvm/test/Transforms/LoopInterchange/interchanged-loop-nest-4.ll
@@ -1,5 +1,5 @@
; REQUIRES: asserts
-; RUN: opt < %s -passes="loop(loop-interchange,loop-interchange)" -cache-line-size=8 -verify-dom-info -verify-loop-info \
+; RUN: opt < %s -passes="loop(loop-interchange,loop-interchange)" -cache-line-size=8 -verify-dom-info -verify-loop-info -loop-interchange-skip-zero-btc=false \
; RUN: -debug-only=loop-interchange 2>&1 | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/llvm/test/Transforms/LoopInterchange/lcssa-phi-outer-latch.ll b/llvm/test/Transforms/LoopInterchange/lcssa-phi-outer-latch.ll
index a5e3accaf8e10..ec02110c90e3b 100644
--- a/llvm/test/Transforms/LoopInterchange/lcssa-phi-outer-latch.ll
+++ b/llvm/test/Transforms/LoopInterchange/lcssa-phi-outer-latch.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 -verify-dom-info -verify-loop-info -verify-scev -verify-loop-lcssa -S | FileCheck %s
+; RUN: opt < %s -passes=loop-interchange -loop-interchange-skip-zero-btc=false -cache-line-size=64 -verify-dom-info -verify-loop-info -verify-scev -verify-loop-lcssa -S | FileCheck %s
; This test is checking that blocks outer.body and outer.latch, where outer.body is the exit
; block of the inner loop and outer.latch the latch of the outer loop, correctly
diff --git a/llvm/test/Transforms/LoopInterchange/pr43176-move-to-new-latch.ll b/llvm/test/Transforms/LoopInterchange/pr43176-move-to-new-latch.ll
index 6b25c3bc9a4ba..56140236825b7 100644
--- a/llvm/test/Transforms/LoopInterchange/pr43176-move-to-new-latch.ll
+++ b/llvm/test/Transforms/LoopInterchange/pr43176-move-to-new-latch.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 -pass-remarks-missed='loop-interchange' -pass-remarks-output=%t -S
+; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 -pass-remarks-missed='loop-interchange' -pass-remarks-output=%t -loop-interchange-skip-zero-btc=false -S
; RUN: FileCheck --input-file=%t %s
@b = external dso_local global [5 x i32], align 16
diff --git a/llvm/test/Transforms/LoopInterchange/pr43326.ll b/llvm/test/Transforms/LoopInterchange/pr43326.ll
index c25c4fadd3042..8ce3d59e3420f 100644
--- a/llvm/test/Transforms/LoopInterchange/pr43326.ll
+++ b/llvm/test/Transforms/LoopInterchange/pr43326.ll
@@ -1,5 +1,5 @@
; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 -pass-remarks-missed='loop-interchange' -pass-remarks-output=%t -S \
-; RUN: -verify-dom-info -verify-loop-info -verify-loop-lcssa -stats 2>&1
+; RUN: -loop-interchange-skip-zero-btc=false -verify-dom-info -verify-loop-info -verify-loop-lcssa -stats 2>&1
; RUN: FileCheck --input-file=%t --check-prefix=REMARKS %s
@a = global i32 0
diff --git a/llvm/test/Transforms/LoopInterchange/pr57148.ll b/llvm/test/Transforms/LoopInterchange/pr57148.ll
index 0d4194762a692..51cad15964ed4 100644
--- a/llvm/test/Transforms/LoopInterchange/pr57148.ll
+++ b/llvm/test/Transforms/LoopInterchange/pr57148.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=loop-interchange -cache-line-size=4 -loop-interchange-threshold=-100 -verify-dom-info -verify-loop-info -verify-scev -verify-loop-lcssa -S | FileCheck %s
+; RUN: opt < %s -passes=loop-interchange -loop-interchange-skip-zero-btc=false -cache-line-size=4 -loop-interchange-threshold=-100 -verify-dom-info -verify-loop-info -verify-scev -verify-loop-lcssa -S | FileCheck %s
; Make sure the loops are in LCSSA form after loop interchange,
; and loop interchange does not hit assertion errors and crash.
diff --git a/llvm/test/Transforms/LoopInterchange/reductions-across-inner-and-outer-loop.ll b/llvm/test/Transforms/LoopInterchange/reductions-across-inner-and-outer-loop.ll
index 27d99e05e84ee..fad6a6e70037c 100644
--- a/llvm/test/Transforms/LoopInterchange/reductions-across-inner-and-outer-loop.ll
+++ b/llvm/test/Transforms/LoopInterchange/reductions-across-inner-and-outer-loop.ll
@@ -1,5 +1,5 @@
; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 -pass-remarks-missed='loop-interchange' -pass-remarks-output=%t -S \
-; RUN: -verify-dom-info -verify-loop-info -verify-loop-lcssa -stats 2>&1 | FileCheck %s
+; RUN: -loop-interchange-skip-zero-btc=false -verify-dom-info -verify-loop-info -verify-loop-lcssa -stats 2>&1 | FileCheck %s
; RUN: FileCheck --input-file=%t --check-prefix=REMARKS %s
>From 0e463b5c9c6dcdf947435972a6ea3c8804d0f13b Mon Sep 17 00:00:00 2001
From: Sjoerd Meijer <smeijer at nvidia.com>
Date: Sat, 8 Nov 2025 01:26:23 -0800
Subject: [PATCH 2/2] Forgto to add test-case
---
.../Transforms/LoopInterchange/zero-btc.ll | 56 +++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 llvm/test/Transforms/LoopInterchange/zero-btc.ll
diff --git a/llvm/test/Transforms/LoopInterchange/zero-btc.ll b/llvm/test/Transforms/LoopInterchange/zero-btc.ll
new file mode 100644
index 0000000000000..58d794e5def40
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/zero-btc.ll
@@ -0,0 +1,56 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=loop-interchange -loop-interchange-profitabilities=ignore -cache-line-size=64 -verify-dom-info -verify-loop-info -verify-scev -verify-loop-lcssa -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+
+; Test case for issue: https://github.com/llvm/llvm-project/issues/163954
+
+define void @test() {
+; CHECK-LABEL: define void @test() {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[OUTER_HEADER:.*]]
+; CHECK: [[OUTER_HEADER]]:
+; CHECK-NEXT: [[I:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[DOTLCSSA:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT: br label %[[INNER_HEADER:.*]]
+; CHECK: [[INNER_HEADER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[J_NEXT:%.*]], %[[INNER_LATCH:.*]] ]
+; CHECK-NEXT: [[TMP0:%.*]] = phi i8 [ [[I]], %[[OUTER_HEADER]] ], [ [[TMP1:%.*]], %[[INNER_LATCH]] ]
+; CHECK-NEXT: br label %[[INNER_BODY:.*]]
+; CHECK: [[INNER_BODY]]:
+; CHECK-NEXT: br i1 true, label %[[INNER_LATCH]], label %[[INNER_BODY]]
+; CHECK: [[INNER_LATCH]]:
+; CHECK-NEXT: [[TMP1]] = or i8 [[TMP0]], 0
+; CHECK-NEXT: [[J_NEXT]] = add i64 [[J]], 1
+; CHECK-NEXT: br i1 true, label %[[OUTER_LATCH]], label %[[INNER_HEADER]]
+; CHECK: [[OUTER_LATCH]]:
+; CHECK-NEXT: [[DOTLCSSA]] = phi i8 [ [[TMP1]], %[[INNER_LATCH]] ]
+; CHECK-NEXT: br i1 true, label %[[EXIT:.*]], label %[[OUTER_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %outer.header
+
+outer.header:
+ %i = phi i8 [ 0, %entry ], [ %1, %outer.latch ]
+ br label %inner.header
+
+inner.header:
+ %j = phi i64 [ 0, %outer.header ], [ %j.next, %inner.latch ]
+ %0 = phi i8 [ %i, %outer.header ], [ %1, %inner.latch ]
+ br label %inner.body
+
+inner.body:
+ br i1 true, label %inner.latch, label %inner.body ; another (self) loop, but never taken
+
+inner.latch:
+ %1 = or i8 %0, 0
+ %j.next = add i64 %j, 1
+ br i1 true, label %outer.latch, label %inner.header
+
+outer.latch:
+ br i1 true, label %exit, label %outer.header
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list