https://github.com/shiltian updated 
https://github.com/llvm/llvm-project/pull/204958

>From e059f901a2b458f0eb8eb3d056d92909697d2093 Mon Sep 17 00:00:00 2001
From: Shilei Tian <[email protected]>
Date: Sat, 20 Jun 2026 00:24:23 -0400
Subject: [PATCH] [SimplifyCFG] Do not thread branches into uncontrolled
 convergent regions

SimplifyCFG's foldCondBranchOnValueKnownInPredecessor can thread an edge past
a block that acts as a reconvergence point. If the threaded destination reaches
an uncontrolled convergent operation before returning to the threaded-through
block, the transform can change which dynamic instance of the convergent
operation is executed.

Add a conservative destination scan for this fold and skip the threading
candidate when it can reach an uncontrolled convergent call before returning
to the original block. Controlled convergent operations using convergence
control tokens are left alone.

Fixes ROCM-26496.
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     | 62 +++++++++++++++++++
 .../SimplifyCFG/convergent-jump-threading.ll  | 57 +++++++++++++++--
 2 files changed, 115 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp 
b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index ca6ae94a9a8d9..3cb989bbd1764 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -3543,6 +3543,61 @@ static ConstantInt *getKnownValueOnEdge(Value *V, 
BasicBlock *From,
   return nullptr;
 }
 
+static bool isUncontrolledConvergentCall(CallBase *CB) {
+  return CB->isConvergent() && !isa<ConvergenceControlInst>(CB) &&
+         !CB->getConvergenceControlToken();
+}
+
+static bool reachesUncontrolledConvergentCallBeforeBlock(BasicBlock *From,
+                                                         BasicBlock *StopBB) {
+  // Walk predecessors of StopBB to find blocks that can reach it. Only
+  // convergent calls on a cycle with StopBB matter - a convergent call on a
+  // path to function exit cannot have its dynamic instance changed by
+  // threading.
+  SmallPtrSet<BasicBlock *, 8> CanReachStop;
+  SmallVector<BasicBlock *, 8> Worklist;
+  for (BasicBlock *Pred : predecessors(StopBB))
+    Worklist.push_back(Pred);
+
+  while (!Worklist.empty()) {
+    BasicBlock *BB = Worklist.pop_back_val();
+    if (BB == StopBB)
+      continue;
+    if (!CanReachStop.insert(BB).second)
+      continue;
+    if (CanReachStop.size() > MaxJumpThreadingLiveBlocks)
+      return true;
+    append_range(Worklist, predecessors(BB));
+  }
+
+  if (!CanReachStop.contains(From))
+    return false;
+
+  SmallPtrSet<BasicBlock *, 8> Visited;
+  Worklist.push_back(From);
+
+  while (!Worklist.empty()) {
+    BasicBlock *BB = Worklist.pop_back_val();
+    if (BB == StopBB || !CanReachStop.contains(BB))
+      continue;
+
+    if (!Visited.insert(BB).second)
+      continue;
+    if (Visited.size() > MaxJumpThreadingLiveBlocks)
+      return true;
+
+    for (Instruction &I : *BB) {
+      auto *CB = dyn_cast<CallBase>(&I);
+      if (CB && isUncontrolledConvergentCall(CB))
+        return true;
+    }
+
+    append_range(Worklist, successors(BB));
+  }
+
+  return false;
+}
+
 /// If we have a conditional branch on something for which we know the constant
 /// value in predecessors (e.g. a phi node in the current block), thread edges
 /// from the predecessor to their ultimate destination.
@@ -3619,6 +3674,13 @@ foldCondBranchOnValueKnownInPredecessorImpl(CondBrInst 
*BI, DomTreeUpdater *DTU,
     if (ReachesNonLocalUseBlocks.contains(RealDest))
       continue;
 
+    // Threading through a branch can bypass a reconvergence point. If the
+    // destination can execute an uncontrolled convergent operation before
+    // returning to this block, this may change the dynamic instance of that
+    // operation.
+    if (reachesUncontrolledConvergentCallBeforeBlock(RealDest, BB))
+      continue;
+
     LLVM_DEBUG({
       dbgs() << "Condition " << *Cond << " in " << BB->getName()
              << " has value " << *Pair.first << " in predecessors:\n";
diff --git a/llvm/test/Transforms/SimplifyCFG/convergent-jump-threading.ll 
b/llvm/test/Transforms/SimplifyCFG/convergent-jump-threading.ll
index e1ce4a6745f43..7d8d2991a3bcf 100644
--- a/llvm/test/Transforms/SimplifyCFG/convergent-jump-threading.ll
+++ b/llvm/test/Transforms/SimplifyCFG/convergent-jump-threading.ll
@@ -12,13 +12,15 @@ define void @preserve_loop_header_branch(i1 %cond, ptr 
%ptr) convergent {
 ; CHECK-NEXT:    br i1 [[COND]], label %[[OUTER_THEN:.*]], label 
%[[LOOP_BODY:.*]]
 ; CHECK:       [[OUTER_THEN]]:
 ; CHECK-NEXT:    store i32 1, ptr [[PTR]], align 4
-; CHECK-NEXT:    br label %[[LOOP_THEN:.*]]
-; CHECK:       [[LOOP_THEN]]:
-; CHECK-NEXT:    store i32 2, ptr [[PTR]], align 4
 ; CHECK-NEXT:    br label %[[LOOP_BODY]]
 ; CHECK:       [[LOOP_BODY]]:
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP_THEN:.*]], label 
%[[LOOP_BODY1:.*]]
+; CHECK:       [[LOOP_THEN]]:
+; CHECK-NEXT:    store i32 2, ptr [[PTR]], align 4
+; CHECK-NEXT:    br label %[[LOOP_BODY1]]
+; CHECK:       [[LOOP_BODY1]]:
 ; CHECK-NEXT:    call void @barrier() #[[ATTR0]]
-; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP_THEN]], label %[[EXIT:.*]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP_BODY]], label %[[EXIT:.*]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
 ;
@@ -92,3 +94,50 @@ loop.body:
 exit:
   ret void
 }
+
+define void @thread_convergent_only_on_exit_path(i1 %cond, ptr %ptr) 
convergent {
+; CHECK-LABEL: define void @thread_convergent_only_on_exit_path(
+; CHECK-SAME: i1 [[COND:%.*]], ptr [[PTR:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @plain()
+; CHECK-NEXT:    br i1 [[COND]], label %[[OUTER_THEN:.*]], label 
%[[EXIT_CRITEDGE:.*]]
+; CHECK:       [[OUTER_THEN]]:
+; CHECK-NEXT:    store i32 1, ptr [[PTR]], align 4
+; CHECK-NEXT:    br label %[[LOOP_THEN:.*]]
+; CHECK:       [[LOOP_THEN]]:
+; CHECK-NEXT:    store i32 2, ptr [[PTR]], align 4
+; CHECK-NEXT:    call void @plain()
+; CHECK-NEXT:    br i1 [[COND]], label %[[LOOP_THEN]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT_CRITEDGE]]:
+; CHECK-NEXT:    call void @plain()
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    call void @barrier() #[[ATTR0]]
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %outer
+
+outer:
+  call void @plain()
+  br i1 %cond, label %outer.then, label %loop.header
+
+outer.then:
+  store i32 1, ptr %ptr, align 4
+  br label %loop.header
+
+loop.header:
+  br i1 %cond, label %loop.then, label %loop.body
+
+loop.then:
+  store i32 2, ptr %ptr, align 4
+  br label %loop.body
+
+loop.body:
+  call void @plain()
+  br i1 %cond, label %loop.header, label %exit
+
+exit:
+  call void @barrier() convergent
+  ret void
+}

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to