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

>From 71956dc466232dd8bd3bf81657a3360d4328f053 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     | 74 +++++++++++++++++--
 .../AMDGPU/convergent-jump-threading.ll       | 57 +++++++++++++-
 2 files changed, 121 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp 
b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index ca6ae94a9a8d9..e588ef91ec7f2 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -3543,13 +3543,67 @@ 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.
-static std::optional<bool>
-foldCondBranchOnValueKnownInPredecessorImpl(CondBrInst *BI, DomTreeUpdater 
*DTU,
-                                            const DataLayout &DL,
-                                            AssumptionCache *AC) {
+static std::optional<bool> foldCondBranchOnValueKnownInPredecessorImpl(
+    CondBrInst *BI, const TargetTransformInfo &TTI, DomTreeUpdater *DTU,
+    AssumptionCache *AC, const DataLayout &DL) {
   SmallMapVector<ConstantInt *, SmallSetVector<BasicBlock *, 2>, 2> 
KnownValues;
   BasicBlock *BB = BI->getParent();
   Value *Cond = BI->getCondition();
@@ -3619,6 +3673,14 @@ 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 (TTI.hasBranchDivergence(BB->getParent()) &&
+        reachesUncontrolledConvergentCallBeforeBlock(RealDest, BB))
+      continue;
+
     LLVM_DEBUG({
       dbgs() << "Condition " << *Cond << " in " << BB->getName()
              << " has value " << *Pair.first << " in predecessors:\n";
@@ -3740,8 +3802,8 @@ bool 
SimplifyCFGOpt::foldCondBranchOnValueKnownInPredecessor(CondBrInst *BI) {
   bool EverChanged = false;
   do {
     // Note that None means "we changed things, but recurse further."
-    Result =
-        foldCondBranchOnValueKnownInPredecessorImpl(BI, DTU, DL, Options.AC);
+    Result = foldCondBranchOnValueKnownInPredecessorImpl(BI, TTI, DTU,
+                                                         Options.AC, DL);
     EverChanged |= Result == std::nullopt || *Result;
   } while (Result == std::nullopt);
   return EverChanged;
diff --git 
a/llvm/test/Transforms/SimplifyCFG/AMDGPU/convergent-jump-threading.ll 
b/llvm/test/Transforms/SimplifyCFG/AMDGPU/convergent-jump-threading.ll
index 2ceb3325263b3..ba3845545a60f 100644
--- a/llvm/test/Transforms/SimplifyCFG/AMDGPU/convergent-jump-threading.ll
+++ b/llvm/test/Transforms/SimplifyCFG/AMDGPU/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