[llvm] [SimpleLoopUnswitch][NFC] Factor out conditional clone legality check (PR #217884)

Keno Fischer via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 21 04:29:31 PDT 2026


https://github.com/Keno created https://github.com/llvm/llvm-project/pull/217884

Move the caller-side legality checks of isSafeForNoNTrivialUnswitching (no token values used outside their defining block, no convergent calls, plus the existing Loop::isSafeToClone conditions) into a new Loop::isSafeToCloneConditionally helper, preserving current behavior exactly.

Transformations that may cause cloned loop bodies to execute conditionally need these stricter conditions than plain cloning; a follow-up commit will use the helper from IRCE's LoopConstrainer as well, which is currently missing this check entirely.

Factored out of #151062 by review request.

>From 2cdd7687a0a7d77bf2313f0571388a496e18d35c Mon Sep 17 00:00:00 2001
From: Keno Fischer <keno at juliahub.com>
Date: Fri, 21 Aug 2026 09:37:55 +0000
Subject: [PATCH] [SimpleLoopUnswitch] Factor legality check into
 Loop::isSafeToCloneConditionally (NFC)

Move the caller-side legality checks of isSafeForNoNTrivialUnswitching
(no token values used outside their defining block, no convergent
calls, plus the existing Loop::isSafeToClone conditions) into a new
Loop::isSafeToCloneConditionally helper, preserving current behavior
exactly.

Transformations that may cause cloned loop bodies to execute
conditionally need these stricter conditions than plain cloning; a
follow-up commit will use the helper from IRCE's LoopConstrainer as
well, which is currently missing this check entirely.
---
 llvm/include/llvm/Analysis/LoopInfo.h          |  6 ++++++
 llvm/lib/Analysis/LoopInfo.cpp                 | 18 ++++++++++++++++++
 .../Transforms/Scalar/SimpleLoopUnswitch.cpp   | 12 +-----------
 3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h
index 15c22e43c44bf..bbac76c85f1ed 100644
--- a/llvm/include/llvm/Analysis/LoopInfo.h
+++ b/llvm/include/llvm/Analysis/LoopInfo.h
@@ -328,6 +328,12 @@ class LLVM_ABI Loop : public LoopBase<BasicBlock, Loop> {
   /// Return true if the loop body is safe to clone in practice.
   bool isSafeToClone() const;
 
+  /// Like `isSafeToClone`, but for transformations where the cloned loop
+  /// bodies may run conditionally. This additionally rejects convergent
+  /// calls (which must not gain new control dependencies) and token values
+  /// that are used outside their defining block.
+  bool isSafeToCloneConditionally() const;
+
   /// Returns true if the loop is annotated parallel.
   ///
   /// A parallel loop can be assumed to not contain any dependencies between
diff --git a/llvm/lib/Analysis/LoopInfo.cpp b/llvm/lib/Analysis/LoopInfo.cpp
index 16a7690092ff5..ab535e302b305 100644
--- a/llvm/lib/Analysis/LoopInfo.cpp
+++ b/llvm/lib/Analysis/LoopInfo.cpp
@@ -530,6 +530,24 @@ bool Loop::isSafeToClone() const {
   return true;
 }
 
+bool Loop::isSafeToCloneConditionally() const {
+  if (!isSafeToClone())
+    return false;
+
+  for (BasicBlock *BB : this->blocks()) {
+    for (Instruction &I : *BB) {
+      if (I.getType()->isTokenTy() && I.isUsedOutsideOfBlock(BB))
+        return false;
+      if (auto *CB = dyn_cast<CallBase>(&I)) {
+        assert(!CB->cannotDuplicate() && "Checked by isSafeToClone().");
+        if (CB->isConvergent())
+          return false;
+      }
+    }
+  }
+  return true;
+}
+
 MDNode *Loop::getLoopID() const {
   MDNode *LoopID = nullptr;
 
diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
index 9425c8a8ac520..619e48f0ce509 100644
--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
+++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
@@ -3359,18 +3359,8 @@ static bool collectUnswitchCandidatesWithInjections(
 }
 
 static bool isSafeForNoNTrivialUnswitching(Loop &L, LoopInfo &LI) {
-  if (!L.isSafeToClone())
+  if (!L.isSafeToCloneConditionally())
     return false;
-  for (auto *BB : L.blocks())
-    for (auto &I : *BB) {
-      if (I.getType()->isTokenTy() && I.isUsedOutsideOfBlock(BB))
-        return false;
-      if (auto *CB = dyn_cast<CallBase>(&I)) {
-        assert(!CB->cannotDuplicate() && "Checked by L.isSafeToClone().");
-        if (CB->isConvergent())
-          return false;
-      }
-    }
 
   // Check if there are irreducible CFG cycles in this loop. If so, we cannot
   // easily unswitch non-trivial edges out of the loop. Doing so might turn the



More information about the llvm-commits mailing list