[llvm] [Analysis] Ignore convergence tokens in dead branches in CodeMetrics (PR #193590)
Finn Plummer via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 22 13:52:09 PDT 2026
https://github.com/inbelic created https://github.com/llvm/llvm-project/pull/193590
In `extendsConvergenceOutsideLoop`, skip users whose parent block is terminated by unreachable, as these represent dead branches. This prevents convergence control tokens in dead code from incorrectly marking a loop as having extended convergence, which could block loop unrolling.
Assisted by: Claude Opus 4.6
>From ccfea43f557ded486f3764c1d5968168b04456e9 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 22 Apr 2026 19:20:46 +0000
Subject: [PATCH] [Analysis] Ignore convergence tokens in dead branches in
CodeMetrics
In `extendsConvergenceOutsideLoop`, skip users whose parent block is
terminated by unreachable, as these represent dead branches. This
prevents convergence control tokens in dead code from incorrectly
marking a loop as having extended convergence, which could block
loop unrolling.
---
llvm/lib/Analysis/CodeMetrics.cpp | 8 ++-
.../LoopUnroll/convergent.controlled.ll | 58 +++++++++++++++++++
2 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/CodeMetrics.cpp b/llvm/lib/Analysis/CodeMetrics.cpp
index ea67b526423bf..21f5ff7319ceb 100644
--- a/llvm/lib/Analysis/CodeMetrics.cpp
+++ b/llvm/lib/Analysis/CodeMetrics.cpp
@@ -16,6 +16,7 @@
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/InstructionCost.h"
@@ -118,7 +119,12 @@ static bool extendsConvergenceOutsideLoop(const Instruction &I, const Loop *L) {
if (!isa<ConvergenceControlInst>(I))
return false;
for (const auto *U : I.users()) {
- if (!L->contains(cast<Instruction>(U)))
+ const auto *UserInst = cast<Instruction>(U);
+ // Ignore users in dead branches, identified by blocks terminated with
+ // unreachable.
+ if (isa<UnreachableInst>(UserInst->getParent()->getTerminator()))
+ continue;
+ if (!L->contains(UserInst))
return true;
}
return false;
diff --git a/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll b/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll
index 5dc613e733f00..6836aea1ff61f 100644
--- a/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll
+++ b/llvm/test/Transforms/LoopUnroll/convergent.controlled.ll
@@ -555,6 +555,64 @@ exit:
ret i32 0
}
+; A convergence token defined in the loop is used outside the loop, but only
+; in a dead branch (block terminated by unreachable). This should not prevent
+; unrolling.
+define i32 @extended_loop_dead_branch(i32 %n) {
+; CHECK-LABEL: @extended_loop_dead_branch(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = freeze i32 [[N:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], -1
+; CHECK-NEXT: [[XTRAITER:%.*]] = and i32 [[TMP0]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], 1
+; CHECK-NEXT: br i1 [[TMP2]], label [[L3_EPIL_PREHEADER:%.*]], label [[ENTRY_NEW:%.*]]
+; CHECK: entry.new:
+; CHECK-NEXT: [[UNROLL_ITER:%.*]] = sub i32 [[TMP0]], [[XTRAITER]]
+; CHECK-NEXT: br label [[L3:%.*]], !llvm.loop [[LOOP4]]
+; CHECK: l3:
+; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 0, [[ENTRY_NEW]] ], [ [[INC_1:%.*]], [[L3]] ]
+; CHECK-NEXT: [[NITER:%.*]] = phi i32 [ 0, [[ENTRY_NEW]] ], [ [[NITER_NEXT_1:%.*]], [[L3]] ]
+; CHECK-NEXT: [[TOK_LOOP:%.*]] = call token @llvm.experimental.convergence.anchor()
+; CHECK-NEXT: call void @f() [ "convergencectrl"(token [[TOK_LOOP]]) ]
+; CHECK-NEXT: [[TOK_LOOP_1:%.*]] = call token @llvm.experimental.convergence.anchor()
+; CHECK-NEXT: call void @f() [ "convergencectrl"(token [[TOK_LOOP_1]]) ]
+; CHECK-NEXT: [[INC_1]] = add nsw i32 [[X_0]], 2
+; CHECK-NEXT: [[NITER_NEXT_1]] = add i32 [[NITER]], 2
+; CHECK-NEXT: [[NITER_NCMP_1:%.*]] = icmp eq i32 [[NITER_NEXT_1]], [[UNROLL_ITER]]
+; CHECK-NEXT: br i1 [[NITER_NCMP_1]], label [[EXIT_UNR_LCSSA:%.*]], label [[L3]], !llvm.loop [[LOOP10:![0-9]+]]
+; CHECK: exit.unr-lcssa:
+; CHECK-NEXT: [[LCMP_MOD:%.*]] = icmp ne i32 [[XTRAITER]], 0
+; CHECK-NEXT: br i1 [[LCMP_MOD]], label [[L3_EPIL_PREHEADER]], label [[EXIT:%.*]]
+; CHECK: l3.epil.preheader:
+; CHECK-NEXT: [[LCMP_MOD1:%.*]] = icmp ne i32 [[XTRAITER]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[LCMP_MOD1]])
+; CHECK-NEXT: br label [[L3_EPIL:%.*]]
+; CHECK: l3.epil:
+; CHECK-NEXT: [[TOK_LOOP_EPIL:%.*]] = call token @llvm.experimental.convergence.anchor()
+; CHECK-NEXT: call void @f() [ "convergencectrl"(token [[TOK_LOOP_EPIL]]) ]
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ br label %l3, !llvm.loop !1
+
+l3:
+ %x.0 = phi i32 [ 0, %entry ], [ %inc, %l3 ]
+ %tok.loop = call token @llvm.experimental.convergence.anchor()
+ call void @f() [ "convergencectrl"(token %tok.loop) ]
+ %inc = add nsw i32 %x.0, 1
+ %exitcond = icmp eq i32 %inc, %n
+ br i1 %exitcond, label %exit, label %l3, !llvm.loop !1
+
+exit:
+ ret i32 0
+
+dead:
+ call void @f() [ "convergencectrl"(token %tok.loop) ]
+ unreachable
+}
+
declare token @llvm.experimental.convergence.anchor()
declare token @llvm.experimental.convergence.loop()
More information about the llvm-commits
mailing list