[llvm] 7eb8f49 - [LoopInterchange] Reject if loops contain indirectbr instruction (#203836)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 15 05:45:31 PDT 2026
Author: Ryotaro Kasuga
Date: 2026-06-15T12:45:26Z
New Revision: 7eb8f49d15760233dd038cc8d294b7148e6af68d
URL: https://github.com/llvm/llvm-project/commit/7eb8f49d15760233dd038cc8d294b7148e6af68d
DIFF: https://github.com/llvm/llvm-project/commit/7eb8f49d15760233dd038cc8d294b7148e6af68d.diff
LOG: [LoopInterchange] Reject if loops contain indirectbr instruction (#203836)
In the transformation phase of LoopInterchange, it may call
`InsertPreheaderForLoop` with loop headers to create new loop
preheaders. However, if any of their predecessors branch to them via an
`indirectbr`, the insertion of a preheader will fail, causing the entire
program to crash.
This patch adds an additional check in the legality check so that the
loops are now rejected if any predecessor of their header block contains
an `indirectbr` instruction.
Fixes #203664.
Added:
llvm/test/Transforms/LoopInterchange/indirectbr.ll
Modified:
llvm/lib/Transforms/Scalar/LoopInterchange.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 6975ad264b1ab..440ddb182c272 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -1377,6 +1377,26 @@ bool LoopInterchangeLegality::currentLimitations() {
return true;
}
+ // Currently, we do not support loops that have a predecessor entering the
+ // loop via an indirectbr.
+ for (Loop *L : {OuterLoop, InnerLoop}) {
+ BasicBlock *Header = L->getHeader();
+ for (BasicBlock *Pred : predecessors(Header)) {
+ if (L->contains(Pred))
+ continue;
+ if (isa<IndirectBrInst>(Pred->getTerminator())) {
+ LLVM_DEBUG(
+ dbgs() << "Indirect branch found in the loop predecessor.\n");
+ ORE->emit([&]() {
+ return OptimizationRemarkMissed(DEBUG_TYPE, "IndirectBranchPreheader",
+ L->getStartLoc(), L->getHeader())
+ << "Indirect branch found in the loop predecessor.";
+ });
+ return true;
+ }
+ }
+ }
+
return false;
}
diff --git a/llvm/test/Transforms/LoopInterchange/indirectbr.ll b/llvm/test/Transforms/LoopInterchange/indirectbr.ll
new file mode 100644
index 0000000000000..480a00576e9a7
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/indirectbr.ll
@@ -0,0 +1,101 @@
+; 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 -S | FileCheck %s
+
+
+; The outer loop preheader contains an indirectbr instruction, so we cannot
+; interchange the loops.
+;
+define void @outer_ph_indirectbr(ptr %A) {
+; CHECK-LABEL: define void @outer_ph_indirectbr(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: indirectbr ptr blockaddress(@outer_ph_indirectbr, %[[OUTER_HEADER:.*]]), [label %[[OUTER_HEADER]]]
+; CHECK: [[OUTER_HEADER]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_INC:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT: br label %[[INNER:.*]]
+; CHECK: [[INNER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[J_INC:%.*]], %[[INNER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [4 x i8], ptr [[A]], i64 [[J]], i64 [[I]]
+; CHECK-NEXT: store i8 0, ptr [[GEP]], align 1
+; CHECK-NEXT: [[J_INC]] = add i64 [[J]], 1
+; CHECK-NEXT: [[EC_J:%.*]] = icmp eq i64 [[J_INC]], 4
+; CHECK-NEXT: br i1 [[EC_J]], label %[[OUTER_LATCH]], label %[[INNER]]
+; CHECK: [[OUTER_LATCH]]:
+; CHECK-NEXT: [[I_INC]] = add i64 [[I]], 1
+; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_INC]], 4
+; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[OUTER_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ indirectbr ptr blockaddress(@outer_ph_indirectbr, %outer.header), [ label %outer.header ]
+
+outer.header:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %outer.latch ]
+ br label %inner
+
+inner:
+ %j = phi i64 [ 0, %outer.header ], [ %j.inc, %inner ]
+ %gep = getelementptr inbounds [4 x i8], ptr %A, i64 %j, i64 %i
+ store i8 0, ptr %gep
+ %j.inc = add i64 %j, 1
+ %ec.j = icmp eq i64 %j.inc, 4
+ br i1 %ec.j, label %outer.latch, label %inner
+
+outer.latch:
+ %i.inc = add i64 %i, 1
+ %ec.i = icmp eq i64 %i.inc, 4
+ br i1 %ec.i, label %exit, label %outer.header
+
+exit:
+ ret void
+}
+
+; The inner loop preheader contains an indirectbr instruction, so we cannot
+; interchange the loops.
+;
+define void @inner_ph_indirectbr(ptr %A) {
+; CHECK-LABEL: define void @inner_ph_indirectbr(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[OUTER_HEADER:.*]]
+; CHECK: [[OUTER_HEADER]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[I_INC:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT: indirectbr ptr blockaddress(@inner_ph_indirectbr, %[[INNER:.*]]), [label %[[INNER]]]
+; CHECK: [[INNER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[OUTER_HEADER]] ], [ [[J_INC:%.*]], %[[INNER]] ]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [4 x i8], ptr [[A]], i64 [[J]], i64 [[I]]
+; CHECK-NEXT: store i8 0, ptr [[GEP]], align 1
+; CHECK-NEXT: [[J_INC]] = add i64 [[J]], 1
+; CHECK-NEXT: [[EC_J:%.*]] = icmp eq i64 [[J_INC]], 4
+; CHECK-NEXT: br i1 [[EC_J]], label %[[OUTER_LATCH]], label %[[INNER]]
+; CHECK: [[OUTER_LATCH]]:
+; CHECK-NEXT: [[I_INC]] = add i64 [[I]], 1
+; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_INC]], 4
+; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[OUTER_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %outer.header
+
+outer.header:
+ %i = phi i64 [ 0, %entry ], [ %i.inc, %outer.latch ]
+ indirectbr ptr blockaddress(@inner_ph_indirectbr, %inner), [ label %inner ]
+
+inner:
+ %j = phi i64 [ 0, %outer.header ], [ %j.inc, %inner ]
+ %gep = getelementptr inbounds [4 x i8], ptr %A, i64 %j, i64 %i
+ store i8 0, ptr %gep
+ %j.inc = add i64 %j, 1
+ %ec.j = icmp eq i64 %j.inc, 4
+ br i1 %ec.j, label %outer.latch, label %inner
+
+outer.latch:
+ %i.inc = add i64 %i, 1
+ %ec.i = icmp eq i64 %i.inc, 4
+ br i1 %ec.i, label %exit, label %outer.header
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list