[llvm] [LoopSplitUtils] Do not clone loops which are unsafe to clone (PR #214258)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 08:34:54 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Sean Clarke (xarkenz)

<details>
<summary>Changes</summary>

Splitting a loop clones it; however, some instructions are not allowed to be cloned, e.g. `indirectbr` and calls to `noduplicate` functions. Use `Loop::isSafeToClone` to bail in such cases.

---
Full diff: https://github.com/llvm/llvm-project/pull/214258.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Utils/LoopSplitUtils.cpp (+6) 
- (added) llvm/test/Transforms/LoopSplit/unsafe-clone.ll (+84) 


``````````diff
diff --git a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp b/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
index db07e3f571f66..7e96367400904 100644
--- a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
+++ b/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
@@ -223,6 +223,12 @@ bool LoopSplitUtils::isLegal() {
     return false;
   }
 
+  // Splitting a loop clones it, so cloning must be safe.
+  if (!L->isSafeToClone()) {
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE ": loop not safe to clone\n");
+    return false;
+  }
+
   // A computable backedge-taken count fixes the iteration space we rebuild.
   const SCEV *BTC = SE->getBackedgeTakenCount(L);
   if (isa<SCEVCouldNotCompute>(BTC)) {
diff --git a/llvm/test/Transforms/LoopSplit/unsafe-clone.ll b/llvm/test/Transforms/LoopSplit/unsafe-clone.ll
new file mode 100644
index 0000000000000..64c6abbd924ea
--- /dev/null
+++ b/llvm/test/Transforms/LoopSplit/unsafe-clone.ll
@@ -0,0 +1,84 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=loop-split-utils -loop-split-points=4 -S < %s | FileCheck %s
+
+declare void @use(i32)
+declare void @prevent_clone(i32) noduplicate
+
+define void @cannot_clone_noduplicate_call(i32 %n) {
+; CHECK-LABEL: define void @cannot_clone_noduplicate_call(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY_LS1:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
+; CHECK:       [[LOOP_LS1]]:
+; CHECK-NEXT:    [[IV_LS1:%.*]] = phi i32 [ 0, %[[ENTRY_LS1]] ], [ [[IV_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
+; CHECK-NEXT:    call void @prevent_clone(i32 [[IV_LS1]])
+; CHECK-NEXT:    [[IV_NEXT_LS1]] = add i32 [[IV_LS1]], 1
+; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp slt i32 [[IV_NEXT_LS1]], [[N]]
+; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
+; CHECK:       [[LS_EXIT1]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+  call void @prevent_clone(i32 %iv)
+  %iv.next = add i32 %iv, 1
+  %cmp = icmp slt i32 %iv.next, %n
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define i32 @cannot_clone_indirectbr(i32 %n, i1 %which) {
+; CHECK-LABEL: define i32 @cannot_clone_indirectbr(
+; CHECK-SAME: i32 [[N:%.*]], i1 [[WHICH:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[BR_ADDR:%.*]] = select i1 [[WHICH]], ptr blockaddress(@cannot_clone_indirectbr, %[[BLOCK_A:.*]]), ptr blockaddress(@cannot_clone_indirectbr, %[[BLOCK_B:.*]])
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LS1:.*]] ]
+; CHECK-NEXT:    indirectbr ptr [[BR_ADDR]], [label %[[BLOCK_A]], label %[[BLOCK_B]]]
+; CHECK:       [[BLOCK_A]]:
+; CHECK-NEXT:    [[VAL_A:%.*]] = add i32 [[IV]], 100
+; CHECK-NEXT:    br label %[[LOOP_LS1]]
+; CHECK:       [[BLOCK_B]]:
+; CHECK-NEXT:    [[VAL_B:%.*]] = add i32 [[IV]], 200
+; CHECK-NEXT:    br label %[[LOOP_LS1]]
+; CHECK:       [[LOOP_LS1]]:
+; CHECK-NEXT:    [[VAL_LS1:%.*]] = phi i32 [ [[VAL_A]], %[[BLOCK_A]] ], [ [[VAL_B]], %[[BLOCK_B]] ]
+; CHECK-NEXT:    call void @use(i32 [[VAL_LS1]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[IV_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP]], label %[[LS_FINAL_EXIT:.*]]
+; CHECK:       [[LS_FINAL_EXIT]]:
+; CHECK-NEXT:    ret i32 0
+;
+entry:
+  %br.addr = select i1 %which, ptr blockaddress(@cannot_clone_indirectbr, %block.a), ptr blockaddress(@cannot_clone_indirectbr, %block.b)
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %latch ]
+  indirectbr ptr %br.addr, [label %block.a, label %block.b]
+
+block.a:
+  %val.a = add i32 %iv, 100
+  br label %latch
+
+block.b:
+  %val.b = add i32 %iv, 200
+  br label %latch
+
+latch:
+  %val = phi i32 [ %val.a, %block.a ], [ %val.b, %block.b ]
+  call void @use(i32 %val)
+  %iv.next = add i32 %iv, 1
+  %cmp = icmp slt i32 %iv.next, %n
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret i32 0
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/214258


More information about the llvm-commits mailing list