Skip to content

Conversation

@jdenny-ornl
Copy link
Collaborator

When LoopUnroll copies the original loop's latch to the corresponding non-latch branch in an unrolled iteration, any !llvm.loop is copied along with it, but !llvm.loop is useless and misleading there. This patch discards it.

e06831a did the same for LoopPeel.

When LoopUnroll copies the original loop's latch to the corresponding
non-latch branch in an unrolled iteration, any `!llvm.loop` is copied
along with it, but `!llvm.loop` is useless and misleading there.  This
patch discards it.

e06831a did the same for LoopPeel.
@llvmbot
Copy link
Member

llvmbot commented Oct 30, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Joel E. Denny (jdenny-ornl)

Changes

When LoopUnroll copies the original loop's latch to the corresponding non-latch branch in an unrolled iteration, any !llvm.loop is copied along with it, but !llvm.loop is useless and misleading there. This patch discards it.

e06831a did the same for LoopPeel.


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

6 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/LoopUnroll.cpp (+6)
  • (modified) llvm/test/Transforms/LoopUnroll/convergent.controlled.ll (+9-9)
  • (modified) llvm/test/Transforms/LoopUnroll/followup.ll (+4-4)
  • (modified) llvm/test/Transforms/LoopUnroll/pr131465.ll (+3-5)
  • (added) llvm/test/Transforms/LoopUnroll/unroll-dont-copy-latch-loop-id.ll (+90)
  • (modified) llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll (+3-5)
diff --git a/llvm/lib/Transforms/Utils/LoopUnroll.cpp b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
index 4fe736ac29b0a..8bbc857715d27 100644
--- a/llvm/lib/Transforms/Utils/LoopUnroll.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
@@ -919,6 +919,12 @@ llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
     Latches[i]->getTerminator()->replaceSuccessorWith(Headers[i], Headers[j]);
   }
 
+  // Remove loop metadata copied from the original loop latch to branches that
+  // are no longer latches.
+  for (unsigned I = 0, E = Latches.size() - (CompletelyUnroll ? 0 : 1); I < E;
+       ++I)
+    Latches[I]->getTerminator()->setMetadata(LLVMContext::MD_loop, nullptr);
+
   // Update dominators of blocks we might reach through exits.
   // Immediate dominator of such block might change, because we add more
   // routes which can lead to the exit: we can now reach it from the copied
diff --git a/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll b/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll
index 6e600d2a659da..5dc613e733f00 100644
--- a/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll
+++ b/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll
@@ -491,41 +491,41 @@ define i32 @unroll_nest(i32 %n, i1 %cond) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    br label [[L3:%.*]]
 ; CHECK:       l3:
-; CHECK-NEXT:    br label [[L2:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br label [[L2:%.*]]
 ; CHECK:       l2:
 ; CHECK-NEXT:    [[TOK_L2:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2]]) ]
-; CHECK-NEXT:    br i1 [[COND:%.*]], label [[L2_1:%.*]], label [[LATCH:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br i1 [[COND:%.*]], label [[L2_1:%.*]], label [[LATCH:%.*]]
 ; CHECK:       l2.1:
 ; CHECK-NEXT:    [[TOK_L2_1:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2_1]]) ]
 ; CHECK-NEXT:    br i1 [[COND]], label [[L2]], label [[LATCH]], !llvm.loop [[LOOP9:![0-9]+]]
 ; CHECK:       latch:
-; CHECK-NEXT:    br label [[L2_12:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br label [[L2_12:%.*]]
 ; CHECK:       l2.12:
 ; CHECK-NEXT:    [[TOK_L2_11:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2_11]]) ]
-; CHECK-NEXT:    br i1 [[COND]], label [[L2_1_1:%.*]], label [[LATCH_1:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br i1 [[COND]], label [[L2_1_1:%.*]], label [[LATCH_1:%.*]]
 ; CHECK:       l2.1.1:
 ; CHECK-NEXT:    [[TOK_L2_1_1:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2_1_1]]) ]
 ; CHECK-NEXT:    br i1 [[COND]], label [[L2_12]], label [[LATCH_1]], !llvm.loop [[LOOP9]]
 ; CHECK:       latch.1:
-; CHECK-NEXT:    br label [[L2_2:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br label [[L2_2:%.*]]
 ; CHECK:       l2.2:
 ; CHECK-NEXT:    [[TOK_L2_2:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2_2]]) ]
-; CHECK-NEXT:    br i1 [[COND]], label [[L2_1_2:%.*]], label [[LATCH_2:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br i1 [[COND]], label [[L2_1_2:%.*]], label [[LATCH_2:%.*]]
 ; CHECK:       l2.1.2:
 ; CHECK-NEXT:    [[TOK_L2_1_2:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2_1_2]]) ]
 ; CHECK-NEXT:    br i1 [[COND]], label [[L2_2]], label [[LATCH_2]], !llvm.loop [[LOOP9]]
 ; CHECK:       latch.2:
-; CHECK-NEXT:    br label [[L2_3:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br label [[L2_3:%.*]]
 ; CHECK:       l2.3:
 ; CHECK-NEXT:    [[TOK_L2_3:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2_3]]) ]
-; CHECK-NEXT:    br i1 [[COND]], label [[L2_1_3:%.*]], label [[LATCH_3:%.*]], !llvm.loop [[LOOP4]]
+; CHECK-NEXT:    br i1 [[COND]], label [[L2_1_3:%.*]], label [[LATCH_3:%.*]]
 ; CHECK:       l2.1.3:
 ; CHECK-NEXT:    [[TOK_L2_1_3:%.*]] = call token @llvm.experimental.convergence.anchor()
 ; CHECK-NEXT:    call void @f() [ "convergencectrl"(token [[TOK_L2_1_3]]) ]
@@ -541,7 +541,7 @@ l3:
   %tok.loop = call token @llvm.experimental.convergence.anchor()
   %inc = add nsw i32 %x.0, 1
   %exitcond = icmp eq i32 %inc, 4
-  br label %l2, !llvm.loop !1
+  br label %l2
 
 l2:
   %tok.l2 = call token @llvm.experimental.convergence.anchor()
diff --git a/llvm/test/Transforms/LoopUnroll/followup.ll b/llvm/test/Transforms/LoopUnroll/followup.ll
index 051e43d52b3be..b6561781d29ca 100644
--- a/llvm/test/Transforms/LoopUnroll/followup.ll
+++ b/llvm/test/Transforms/LoopUnroll/followup.ll
@@ -36,11 +36,11 @@ for.end:                                          ; preds = %for.body, %entry
 ; COMMON-LABEL: @test(
 
 
-; COUNT: br i1 %exitcond.1, label %for.end.loopexit, label %for.body, !llvm.loop ![[LOOP:[0-9]+]]
+; COUNT: br i1 %exitcond.1, label %for.end.loopexit, label %for.body, !llvm.loop ![[#LOOP:]]
 
-; COUNT: ![[FOLLOWUP_ALL:[0-9]+]] = !{!"FollowupAll"}
-; COUNT: ![[FOLLOWUP_UNROLLED:[0-9]+]] = !{!"FollowupUnrolled"}
-; COUNT: ![[LOOP]] = distinct !{![[LOOP]], ![[FOLLOWUP_ALL]], ![[FOLLOWUP_UNROLLED]]}
+; COUNT: ![[#LOOP]] = distinct !{![[#LOOP]], ![[#FOLLOWUP_ALL:]], ![[#FOLLOWUP_UNROLLED:]]}
+; COUNT: ![[#FOLLOWUP_ALL]] = !{!"FollowupAll"}
+; COUNT: ![[#FOLLOWUP_UNROLLED]] = !{!"FollowupUnrolled"}
 
 
 ; EPILOG: br i1 %niter.ncmp.7, label %for.end.loopexit.unr-lcssa, label %for.body, !llvm.loop ![[LOOP_0:[0-9]+]]
diff --git a/llvm/test/Transforms/LoopUnroll/pr131465.ll b/llvm/test/Transforms/LoopUnroll/pr131465.ll
index 643b020c6c110..60bea6a4d9043 100644
--- a/llvm/test/Transforms/LoopUnroll/pr131465.ll
+++ b/llvm/test/Transforms/LoopUnroll/pr131465.ll
@@ -11,11 +11,11 @@ define i32 @pr131465(i1 %x) mustprogress {
 ; CHECK-NEXT:    [[INDVAR:%.*]] = phi i32 [ 2, %[[ENTRY]] ], [ [[NEXT_1:%.*]], %[[FOR_BODY_1:.*]] ]
 ; CHECK-NEXT:    [[NEXT:%.*]] = add nsw i32 [[INDVAR]], [[INC]]
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i32 [[NEXT]], 2
-; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[FOR_END:.*]], label %[[FOR_BODY_1]], !llvm.loop [[LOOP0:![0-9]+]]
+; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[FOR_END:.*]], label %[[FOR_BODY_1]]
 ; CHECK:       [[FOR_BODY_1]]:
 ; CHECK-NEXT:    [[NEXT_1]] = add nsw i32 [[NEXT]], [[INC]]
 ; CHECK-NEXT:    [[EXITCOND_1:%.*]] = icmp eq i32 [[NEXT_1]], 2
-; CHECK-NEXT:    br i1 [[EXITCOND_1]], label %[[FOR_END]], label %[[FOR_BODY]], !llvm.loop [[LOOP2:![0-9]+]]
+; CHECK-NEXT:    br i1 [[EXITCOND_1]], label %[[FOR_END]], label %[[FOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
 ; CHECK:       [[FOR_END]]:
 ; CHECK-NEXT:    ret i32 0
 ;
@@ -37,7 +37,5 @@ for.end:
 !0 = !{!0, !{!"llvm.loop.unroll.count", i32 2}}
 ;.
 ; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]]}
-; CHECK: [[META1]] = !{!"llvm.loop.unroll.count", i32 2}
-; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[META3:![0-9]+]]}
-; CHECK: [[META3]] = !{!"llvm.loop.unroll.disable"}
+; CHECK: [[META1]] = !{!"llvm.loop.unroll.disable"}
 ;.
diff --git a/llvm/test/Transforms/LoopUnroll/unroll-dont-copy-latch-loop-id.ll b/llvm/test/Transforms/LoopUnroll/unroll-dont-copy-latch-loop-id.ll
new file mode 100644
index 0000000000000..191ebb023b7c1
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/unroll-dont-copy-latch-loop-id.ll
@@ -0,0 +1,90 @@
+; Check that !llvm.loop is not copied from the original loop's latch to the
+; corresponding non-latch branch in any unrolled iteration.
+
+; The -implicit-check-not options make sure that no additional label or
+; !llvm.loop shows up.
+; DEFINE: %{unroll} = opt < %s -passes=loop-unroll -S
+; DEFINE: %{fc} = FileCheck %s \
+; DEFINE:     -implicit-check-not='{{^[^ ;]*:}}' \
+; DEFINE:     -implicit-check-not='!llvm.loop' \
+; DEFINE:     -check-prefixes
+
+; Check partial unroll: only the unrolled loop's latch has !llvm.loop.
+; RUN: %{unroll} -unroll-count=3 | %{fc} ALL,UR,UR3
+
+; Check complete unroll: no !llvm.loop remains because no loop remains.
+; RUN: %{unroll} -unroll-count=4 | %{fc} ALL,UR,UR4
+
+; Check remainder: both loops have a !llvm.loop.
+; DEFINE: %{rt} = %{unroll} -unroll-count=3 -unroll-runtime
+; RUN: %{rt} -unroll-runtime-epilog=true | %{fc} ALL,RT,EPILOG
+; RUN: %{rt} -unroll-runtime-epilog=false | %{fc} ALL,RT,PROLOG
+
+;    ALL: define void @test(i32 %n) {
+;    ALL: entry:
+;     UR:   br label %body
+; EPILOG:   br i1 %{{.*}}, label %body.epil.preheader, label %entry.new
+; PROLOG:   br i1 %{{.*}}, label %body.prol.preheader, label %body.prol.loopexit
+; PROLOG: body.prol.preheader:
+; PROLOG:   br label %body.prol
+; PROLOG: body.prol:
+; PROLOG:   br i1 %{{.*}}, label %body.prol, label %body.prol.loopexit.unr-lcssa, !llvm.loop !0
+; PROLOG: body.prol.loopexit.unr-lcssa:
+; PROLOG:   br label %body.prol.loopexit
+; PROLOG: body.prol.loopexit:
+; PROLOG:   br i1 %{{.*}}, label %exit, label %entry.new
+;     RT: entry.new:
+;     RT:   br label %body
+;    ALL: body:
+;     UR:   br i1 %c, label %body.1, label %exit
+; EPILOG:   br i1 %{{.*}}, label %body, label %exit.unr-lcssa, !llvm.loop !0
+; PROLOG:   br i1 %{{.*}}, label %body, label %exit.unr-lcssa, !llvm.loop !2
+;     UR: body.1:
+;     UR:   br i1 %c.1, label %body.2, label %exit
+;     UR: body.2:
+;    UR3:   br i1 %c.2, label %body, label %exit, !llvm.loop !0
+;    UR4:   br i1 %c.2, label %body.3, label %exit
+;    UR4: body.3:
+;    UR4:   br label %exit
+;     RT: exit.unr-lcssa:
+; EPILOG:   br i1 {{.*}}, label %body.epil.preheader, label %exit
+; PROLOG:   br label %exit
+; EPILOG: body.epil.preheader:
+; EPILOG:   br label %body.epil
+; EPILOG: body.epil:
+; EPILOG:   br i1 {{.*}}, label %body.epil, label %exit.epilog-lcssa, !llvm.loop !3
+; EPILOG: exit.epilog-lcssa:
+; EPILOG:   br label %exit
+;    ALL: exit:
+;    ALL:   ret void
+;    ALL: }
+define void @test(i32 %n) {
+entry:
+  %max = call i32 @llvm.umin.i32(i32 %n, i32 3)
+  br label %body
+
+body:
+  %i = phi i32 [ 0, %entry ], [ %inc, %body ]
+  %inc = add i32 %i, 1
+  %c = icmp ult i32 %i, %max
+  br i1 %c, label %body, label %exit, !llvm.loop !0
+
+exit:
+  ret void
+}
+
+; UR3: !0 = distinct !{!0, !1, !2}
+; UR3: !1 = !{!"copied"}
+; UR3: !2 = !{!"llvm.loop.unroll.disable"}
+;
+; EPILOG: !0 = distinct !{!0, !1, !2}
+; EPILOG: !1 = !{!"copied"}
+; EPILOG: !2 = !{!"llvm.loop.unroll.disable"}
+; EPILOG: !3 = distinct !{!3, !2}
+;
+; PROLOG: !0 = distinct !{!0, !1}
+; PROLOG: !1 = !{!"llvm.loop.unroll.disable"}
+; PROLOG: !2 = distinct !{!2, !3, !1}
+; PROLOG: !3 = !{!"copied"}
+!0 = distinct !{!0, !1}
+!1 = !{!"copied"}
diff --git a/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll b/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll
index f85aac75539c6..77dd3429b684e 100644
--- a/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll
+++ b/llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll
@@ -419,7 +419,7 @@ define void @loop_body_with_dead_blocks(ptr %src) {
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    [[L_2:%.*]] = load i32, ptr [[SRC]], align 8
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp eq i32 [[L_2]], 1
-; CHECK-NEXT:    br i1 [[C_2]], label [[EXIT:%.*]], label [[LOOP_HEADER_1:%.*]], !llvm.loop [[LOOP7:![0-9]+]]
+; CHECK-NEXT:    br i1 [[C_2]], label [[EXIT:%.*]], label [[LOOP_HEADER_1:%.*]]
 ; CHECK:       loop.header.1:
 ; CHECK-NEXT:    br label [[LOOP_BB_1:%.*]]
 ; CHECK:       loop.bb.1:
@@ -429,7 +429,7 @@ define void @loop_body_with_dead_blocks(ptr %src) {
 ; CHECK-NEXT:    call void @foo()
 ; CHECK-NEXT:    [[L_2_1:%.*]] = load i32, ptr [[SRC]], align 8
 ; CHECK-NEXT:    [[C_2_1:%.*]] = icmp eq i32 [[L_2_1]], 1
-; CHECK-NEXT:    br i1 [[C_2_1]], label [[EXIT]], label [[LOOP_HEADER]], !llvm.loop [[LOOP9:![0-9]+]]
+; CHECK-NEXT:    br i1 [[C_2_1]], label [[EXIT]], label [[LOOP_HEADER]], !llvm.loop [[LOOP7:![0-9]+]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
 ;
@@ -471,7 +471,5 @@ exit:
 ; CHECK: [[LOOP4]] = distinct !{[[LOOP4]], [[META1]], [[META2]]}
 ; CHECK: [[LOOP5]] = distinct !{[[LOOP5]], [[META1]], [[META2]]}
 ; CHECK: [[LOOP6]] = distinct !{[[LOOP6]], [[META1]], [[META2]]}
-; CHECK: [[LOOP7]] = distinct !{[[LOOP7]], [[META1]], [[META8:![0-9]+]]}
-; CHECK: [[META8]] = !{!"llvm.loop.unroll.count", i32 2}
-; CHECK: [[LOOP9]] = distinct !{[[LOOP9]], [[META1]], [[META2]]}
+; CHECK: [[LOOP7]] = distinct !{[[LOOP7]], [[META1]], [[META2]]}
 ;.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like the kind of test that is going to really piss me off if I ever have to update it. Either de-magic and reduce the scope of this test, or add a UTC flag that adds {{$}} to the end of lines so absence of metadata can be verified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants