Author: Ehsan Amiri
Date: 2026-09-10T14:57:29-04:00
New Revision: 26609f5b95679e02d2373a7c97ff811872a4b505
URL: https://github.com/llvm/llvm-project/commit/26609f5b95679e02d2373a7c97ff811872a4b505
DIFF: https://github.com/llvm/llvm-project/commit/26609f5b95679e02d2373a7c97ff811872a4b505.diff
LOG: [SimpleLoopUnswitch] Fix bug in partially invariant unswitching (#222118)
Partial invariant unswitching can potentially hoist a load from header
to the preheader of the loop. If a potentially no-return function call
exists in the loop header before the load instruction, the
transformation is still performed, but this is not correct.
Added:
llvm/test/Transforms/SimpleLoopUnswitch/pr219510.ll
Modified:
llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
index abe8d19b1a583..045c259f0d63b 100644
--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
+++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
@@ -2785,21 +2785,27 @@ static bool collectUnswitchCandidates(
AddUnswitchCandidatesForInst(BI, BI->getCondition());
}
- if (MSSAU && !findOptionMDForLoop(&L, "llvm.loop.unswitch.partial.disable") &&
+ BasicBlock *Header = L.getHeader();
+ // Need to make sure the load instruction to be hoisted is always executed.
+ bool HeaderCondGuaranteedToExecute =
+ isGuaranteedToTransferExecutionToSuccessor(
+ Header->begin(), Header->getTerminator()->getIterator());
+ if (MSSAU && HeaderCondGuaranteedToExecute &&
+ !findOptionMDForLoop(&L, "llvm.loop.unswitch.partial.disable") &&
!any_of(UnswitchCandidates, [&L](auto &TerminatorAndInvariants) {
- return TerminatorAndInvariants.TI == L.getHeader()->getTerminator();
- })) {
+ return TerminatorAndInvariants.TI == L.getHeader()->getTerminator();
+ })) {
MemorySSA *MSSA = MSSAU->getMemorySSA();
if (auto Info = hasPartialIVCondition(L, MSSAThreshold, *MSSA, AA)) {
LLVM_DEBUG(
dbgs() << "simple-loop-unswitch: Found partially invariant condition "
<< *Info->InstToDuplicate[0] << "\n");
PartialIVInfo = *Info;
- PartialIVCondBranch = L.getHeader()->getTerminator();
+ PartialIVCondBranch = Header->getTerminator();
TinyPtrVector<Value *> ValsToDuplicate;
llvm::append_range(ValsToDuplicate, Info->InstToDuplicate);
UnswitchCandidates.push_back(
- {L.getHeader()->getTerminator(), std::move(ValsToDuplicate)});
+ {Header->getTerminator(), std::move(ValsToDuplicate)});
}
}
return !UnswitchCandidates.empty();
diff --git a/llvm/test/Transforms/SimpleLoopUnswitch/pr219510.ll b/llvm/test/Transforms/SimpleLoopUnswitch/pr219510.ll
new file mode 100644
index 0000000000000..555bfc1d992cc
--- /dev/null
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/pr219510.ll
@@ -0,0 +1,34 @@
+; RUN: opt -passes='loop-mssa(simple-loop-unswitch<nontrivial>)' -S < %s | FileCheck %s
+
+declare void @barrier() memory(read)
+declare void @clobber()
+
+; CHECK: call void @barrier()
+; CHECK-NEXT: %lv = load i32, ptr %ptr
+
+
+define i32 @bad_unswitch(ptr %ptr, i32 %N) {
+entry:
+ br label %loop.header
+loop.header:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ call void @barrier()
+ %lv = load i32, ptr %ptr
+ %sc = icmp eq i32 %lv, 100
+ br i1 %sc, label %noclobber, label %clobber
+
+noclobber:
+ br label %loop.latch
+
+clobber:
+ call void @clobber()
+ br label %loop.latch
+
+loop.latch:
+ %c = icmp ult i32 %iv, %N
+ %iv.next = add i32 %iv, 1
+ br i1 %c, label %loop.header, label %exit
+
+exit:
+ ret i32 10
+}