[llvm] [SimplifyCFG]divergent undef predecessor removal (PR #204287)

Fujun Han via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 21 21:56:30 PDT 2026


https://github.com/Peter9606 updated https://github.com/llvm/llvm-project/pull/204287

>From f92029a7760a22253d411f256619ecf49169214b Mon Sep 17 00:00:00 2001
From: Fujun Han <fujun.han at iluvatar.com>
Date: Mon, 22 Jun 2026 09:22:09 +0800
Subject: [PATCH] [SimplifyCFG] Skip predecessor removal at convergent join
 points

Poison on one PHI incoming edge must not be treated as proof that the
predecessor is unreachable when the value flows to a noundef argument of a
convergent call. IR represents a single-lane view, but convergent operations
bind SIMT semantics at join points.

Add regression test divergent-poison-phi.ll.

Assisted-by: Cursor (composer-2.5-fast)
Signed-off-by: Fujun Han <fujun.han at iluvatar.com>
---
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |  7 +-
 .../SimplifyCFG/divergent-poison-phi.ll       | 64 +++++++++++++++++++
 2 files changed, 69 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Transforms/SimplifyCFG/divergent-poison-phi.ll

diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 524947dd2e95d..01db2ca0076aa 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -8891,9 +8891,12 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
         if (isa<ConstantPointerNull>(C) &&
             CB->paramHasNonNullAttr(ArgIdx, /*AllowUndefOrPoison=*/false))
           return !PtrValueMayBeModified;
-        // Passing undef to a noundef argument is undefined.
+        // Passing poison/undef to a noundef argument is undefined in the
+        // single-lane IR model, unless the call is convergent: at a join
+        // point, convergent operations bind SIMT semantics and poison on one
+        // PHI incoming edge does not imply that predecessor is unreachable.
         if (isa<UndefValue>(C) && CB->isPassingUndefUB(ArgIdx))
-          return true;
+          return !CB->isConvergent();
       }
     }
     // Div/Rem by zero is immediate UB
diff --git a/llvm/test/Transforms/SimplifyCFG/divergent-poison-phi.ll b/llvm/test/Transforms/SimplifyCFG/divergent-poison-phi.ll
new file mode 100644
index 0000000000000..3b0874adfaceb
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/divergent-poison-phi.ll
@@ -0,0 +1,64 @@
+; REQUIRES: amdgpu-registered-target && x86-registered-target
+; RUN: opt < %s -mtriple=amdgcn -passes=simplifycfg -S | FileCheck %s -check-prefix=DIVERGENT
+; RUN: opt < %s -mtriple=x86_64 -passes=simplifycfg -S | FileCheck %s -check-prefix=UNIFORM
+
+; When poison on a PHI incoming edge flows to a convergent noundef call at a
+; join point, do not treat that edge as unreachable. Scalar folding remains
+; valid for the same pattern with a non-convergent callee.
+
+declare i32 @llvm.amdgcn.workitem.id.x() #0
+
+declare i32 @shuffle(i32 noundef, i32 noundef) convergent
+
+declare i32 @use_val(i32 noundef)
+
+define amdgpu_kernel void @k_convergent(ptr addrspace(1) nocapture %counter, ptr addrspace(1) nocapture %out) #1 {
+; DIVERGENT-LABEL: @k_convergent(
+entry:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %cmp = icmp eq i32 %tid, 0
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  %old = atomicrmw add ptr addrspace(1) %counter, i32 1 monotonic, align 4
+  br label %if.end
+
+if.end:
+  %val = phi i32 [ %old, %if.then ], [ poison, %entry ]
+  %shfl = tail call i32 @shuffle(i32 noundef %val, i32 noundef %tid) #2
+  %idx = zext i32 %tid to i64
+  %ptr = getelementptr i32, ptr addrspace(1) %out, i64 %idx
+  store i32 %shfl, ptr addrspace(1) %ptr, align 4
+  ret void
+}
+
+define void @k_scalar(i32 %tid, ptr nocapture %counter, ptr nocapture %out) {
+; UNIFORM-LABEL: @k_scalar(
+entry:
+  %cmp = icmp eq i32 %tid, 0
+  br i1 %cmp, label %if.then, label %if.end
+
+if.then:
+  %old = atomicrmw add ptr %counter, i32 1 monotonic, align 4
+  br label %if.end
+
+if.end:
+  %val = phi i32 [ %old, %if.then ], [ poison, %entry ]
+  %use = call i32 @use_val(i32 noundef %val)
+  %idx = zext i32 %tid to i64
+  %ptr = getelementptr i32, ptr %out, i64 %idx
+  store i32 %use, ptr %ptr, align 4
+  ret void
+}
+
+; DIVERGENT-NOT: call void @llvm.assume
+; DIVERGENT: br i1 {{%.*}}, label %if.then, label %if.end
+; DIVERGENT: atomicrmw add
+; DIVERGENT: phi i32
+
+; UNIFORM: call void @llvm.assume
+; UNIFORM-NOT: br i1 {{%.*}}, label %if.then, label %if.end
+
+attributes #0 = { nounwind readnone }
+attributes #1 = { convergent "amdgpu-flat-work-group-size"="1,256" }
+attributes #2 = { convergent }



More information about the llvm-commits mailing list