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
