[llvm] [AMDGPU] using divergent/uniform information in ISel of zext (PR #174539)

Zeng Wu via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 30 10:17:14 PST 2026


https://github.com/zwu-2025 updated https://github.com/llvm/llvm-project/pull/174539

>From 07e0ee192b7640291fd0d908f1eb0035a4f2b14c Mon Sep 17 00:00:00 2001
From: Zeng Wu <Zeng.Wu2 at amd.com>
Date: Sun, 11 Jan 2026 01:11:49 -0600
Subject: [PATCH 1/3] [AMD] using divergent/uniform information in ISel of zext

---
 llvm/lib/Target/AMDGPU/SIInstructions.td      | 12 +++++++++++-
 .../AMDGPU/llvm.amdgcn.i1.i32.uniform.zext.ll | 19 +++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/llvm.amdgcn.i1.i32.uniform.zext.ll

diff --git a/llvm/lib/Target/AMDGPU/SIInstructions.td b/llvm/lib/Target/AMDGPU/SIInstructions.td
index ca5a4d7301bda..46a254263c0e4 100644
--- a/llvm/lib/Target/AMDGPU/SIInstructions.td
+++ b/llvm/lib/Target/AMDGPU/SIInstructions.td
@@ -2535,7 +2535,7 @@ def : GCNPat <
 >;
 
 class Ext32Pat <SDNode ext> : GCNPat <
-  (i32 (ext i1:$src0)),
+  (i32 (ext i1:$src0)), 
   (V_CNDMASK_B32_e64 /*src0mod*/(i32 0), /*src0*/(i32 0),
                      /*src1mod*/(i32 0), /*src1*/(i32 1), i1:$src0)
 >;
@@ -3044,6 +3044,11 @@ def : GCNPat <
   (S_AND_B64 $src0, $src1)
 >;
 
+def : GCNPat <
+  (i64 (UniformUnaryFrag<zext> i1:$src)),
+  (S_AND_B64 $src, (i64 1))
+>;
+
 def : GCNPat <
   (i1 (or i1:$src0, i1:$src1)),
   (S_OR_B64 $src0, $src1)
@@ -3083,6 +3088,11 @@ def : GCNPat <
   (S_AND_B32 $src0, $src1)
 >;
 
+def : GCNPat <
+  (i32 (UniformUnaryFrag<zext> i1:$src)),
+  (S_AND_B32 $src, (i32 1))
+>;
+
 def : GCNPat <
   (i1 (or i1:$src0, i1:$src1)),
   (S_OR_B32 $src0, $src1)
diff --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.i1.i32.uniform.zext.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.i1.i32.uniform.zext.ll
new file mode 100644
index 0000000000000..63486534e7032
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.i1.i32.uniform.zext.ll
@@ -0,0 +1,19 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1250 < %s | FileCheck --check-prefix=GCN-OPT %s
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx950 < %s | FileCheck --check-prefix=GCN-OPT %s
+; RUN: llc -global-isel=1 -mtriple=amdgcn -mcpu=gfx1250 < %s | FileCheck --check-prefix=GCN-G_SEL %s
+
+declare i32 @llvm.amdgcn.workitem.id.x() #0
+
+define amdgpu_kernel void @zext_i1_to_i32_uniform(ptr addrspace(1) %out, i1 %pred) #0 {
+entry:
+; GCN-LABEL: zext_i1_to_i32_uniform:
+; GCN-OPT:    s_and_b32 s{{.*}}, s{{.*}}, 1
+; GCN-G_SEL:  v_mov_b32_e32
+; GCN-OPT: s_endpgm
+  %tmp2 = zext i1 %pred to i32
+  store i32 %tmp2, ptr addrspace(1) %out
+  ret void
+}
+
+attributes #0 = { nounwind }

>From 29e1625a531b855192f4d9c96300e5dcd54287f7 Mon Sep 17 00:00:00 2001
From: Zeng Wu <Zeng.Wu2 at amd.com>
Date: Wed, 21 Jan 2026 20:11:35 -0600
Subject: [PATCH 2/3] [AMDGPU] using divergent/uniform information in ISel of
 zext

---
 .../lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 33 ++++++++++++++++---
 llvm/lib/Target/AMDGPU/SIInstructions.td      | 14 +++-----
 2 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
index 4ad721bf21959..9eb8df71ed165 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
@@ -27,6 +27,7 @@
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/PseudoProbe.h"
+#include "llvm/MC/MCInstrDesc.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Target/TargetMachine.h"
 using namespace llvm;
@@ -105,8 +106,29 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
   MVT VT = Op.getSimpleValueType();
 
   // Stick to the preferred register classes for legal types.
-  if (TLI->isTypeLegal(VT))
-    UseRC = TLI->getRegClassFor(VT, Op->isDivergent());
+  if (TLI->isTypeLegal(VT)) {
+      const llvm::TargetRegisterClass *SrcRegRC = TRI->getMinimalPhysRegClass(SrcReg);
+      UseRC = TRI->getCrossCopyRegClass(SrcRegRC);
+      const llvm::TargetRegisterClass *LegalRC =
+          TLI->getRegClassFor(VT, Op->isDivergent());
+
+      if (!TRI->isTypeLegalForClass(*UseRC, VT)) {
+          UseRC = LegalRC;
+      }
+      {
+        // If there is a sub class relation between CrossCopyRegClass and
+        // natively supported RegClass, the result of getRegClassFor, then
+        // we use natively supported RegClass to stick the existing logic.
+        // For example, on AArch64, the CrossCopyRegClass of x0 is `GPR64arg`
+        // and x0 is natively supported in regclass `GPR64all`, then `GPR64all` is chosen.
+        // However, on AMDGPU, for `scc`, the natively supported regclass is, for
+        // some reasons, SGPR_64 but CrossCopyRegClass is SGPR_32. Since there
+        // is subclass relation, CrossCopyRegClass, SGPR_32 is picked.
+        if (TRI->getCommonSubClass(UseRC, LegalRC)) {
+            UseRC = LegalRC;
+        }
+      }
+  }
 
   for (SDNode *User : Op->users()) {
     bool Match = true;
@@ -121,7 +143,7 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
       for (unsigned i = 0, e = User->getNumOperands(); i != e; ++i) {
         if (User->getOperand(i) != Op)
           continue;
-        if (VT == MVT::Other || VT == MVT::Glue)
+        if (VT == MVT::Other || VT == MVT::Glue || !TLI->isTypeLegal(VT))
           continue;
         Match = false;
         if (User->isMachineOpcode()) {
@@ -131,10 +153,11 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
             RC = TRI->getAllocatableClass(
                 TII->getRegClass(II, i + II.getNumDefs()));
           }
+
           if (!UseRC)
             UseRC = RC;
           else if (RC) {
-            const TargetRegisterClass *ComRC =
+              const TargetRegisterClass *ComRC =
                 TRI->getCommonSubClass(UseRC, RC);
             // If multiple uses expect disjoint register classes, we emit
             // copies in AddRegisterOperand.
@@ -153,6 +176,8 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
   SrcRC = TRI->getMinimalPhysRegClass(SrcReg, VT);
 
   // Figure out the register class to create for the destreg.
+  // If SrcReg is phsysical register, the corresponding register class could be
+  // non allocable, so we prefer UseRC to SrcRC
   if (VRBase) {
     DstRC = MRI->getRegClass(VRBase);
   } else if (UseRC) {
diff --git a/llvm/lib/Target/AMDGPU/SIInstructions.td b/llvm/lib/Target/AMDGPU/SIInstructions.td
index 46a254263c0e4..257fa23608d94 100644
--- a/llvm/lib/Target/AMDGPU/SIInstructions.td
+++ b/llvm/lib/Target/AMDGPU/SIInstructions.td
@@ -3038,16 +3038,17 @@ def : FPToI1Pat<V_CMP_EQ_F64_e64, CONST.FP64_NEG_ONE, i64, f64, fp_to_sint>;
 // instructions resulting in the copies from SCC to these instructions
 // will be moved to the VALU.
 
+def : GCNPat <
+ (i32 (UniformUnaryFrag<zext> i1:$src)),
+  (S_AND_B32 $src, (i32 1))
+>;
+
 let WaveSizePredicate = isWave64 in {
 def : GCNPat <
   (i1 (and i1:$src0, i1:$src1)),
   (S_AND_B64 $src0, $src1)
 >;
 
-def : GCNPat <
-  (i64 (UniformUnaryFrag<zext> i1:$src)),
-  (S_AND_B64 $src, (i64 1))
->;
 
 def : GCNPat <
   (i1 (or i1:$src0, i1:$src1)),
@@ -3088,11 +3089,6 @@ def : GCNPat <
   (S_AND_B32 $src0, $src1)
 >;
 
-def : GCNPat <
-  (i32 (UniformUnaryFrag<zext> i1:$src)),
-  (S_AND_B32 $src, (i32 1))
->;
-
 def : GCNPat <
   (i1 (or i1:$src0, i1:$src1)),
   (S_OR_B32 $src0, $src1)

>From d165a018d080589e98b4a34a95e656856c6a4734 Mon Sep 17 00:00:00 2001
From: root <zengwu13 at amd.com>
Date: Fri, 30 Jan 2026 18:16:49 +0000
Subject: [PATCH 3/3] comments

---
 .../lib/CodeGen/SelectionDAG/InstrEmitter.cpp | 43 +++++++++----------
 1 file changed, 20 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
index 9eb8df71ed165..2e661faafbb95 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
@@ -107,27 +107,24 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
 
   // Stick to the preferred register classes for legal types.
   if (TLI->isTypeLegal(VT)) {
-      const llvm::TargetRegisterClass *SrcRegRC = TRI->getMinimalPhysRegClass(SrcReg);
-      UseRC = TRI->getCrossCopyRegClass(SrcRegRC);
-      const llvm::TargetRegisterClass *LegalRC =
+    const llvm::TargetRegisterClass *SrcRegRC = TRI->getMinimalPhysRegClass(SrcReg);
+    const llvm::TargetRegisterClass *CrossCopyRC = TRI->getCrossCopyRegClass(SrcRegRC);
+    const llvm::TargetRegisterClass *LegalRC =
           TLI->getRegClassFor(VT, Op->isDivergent());
 
-      if (!TRI->isTypeLegalForClass(*UseRC, VT)) {
-          UseRC = LegalRC;
-      }
-      {
-        // If there is a sub class relation between CrossCopyRegClass and
-        // natively supported RegClass, the result of getRegClassFor, then
-        // we use natively supported RegClass to stick the existing logic.
-        // For example, on AArch64, the CrossCopyRegClass of x0 is `GPR64arg`
-        // and x0 is natively supported in regclass `GPR64all`, then `GPR64all` is chosen.
-        // However, on AMDGPU, for `scc`, the natively supported regclass is, for
-        // some reasons, SGPR_64 but CrossCopyRegClass is SGPR_32. Since there
-        // is subclass relation, CrossCopyRegClass, SGPR_32 is picked.
-        if (TRI->getCommonSubClass(UseRC, LegalRC)) {
-            UseRC = LegalRC;
-        }
-      }
+    // If there is a sub class relation between CrossCopyRegClass and
+    // natively supported RegClass, the result of getRegClassFor, then
+    // we use natively supported RegClass to stick the existing logic.
+    // For example, on AArch64, the CrossCopyRegClass of x0 is `GPR64arg`
+    // and x0 is natively supported in regclass `GPR64all`, then `GPR64all` is chosen.
+    // However, on AMDGPU, for `scc`, the natively supported regclass is, for
+    // some reasons, SGPR_64 but CrossCopyRegClass is SGPR_32. Since there
+    // is subclass relation, CrossCopyRegClass, SGPR_32 is picked.
+    if (!TRI->isTypeLegalForClass(*CrossCopyRC, VT) || TRI->getCommonSubClass(CrossCopyRC, LegalRC)) {
+      UseRC = LegalRC;
+    } else {
+      UseRC = CrossCopyRC;
+    }
   }
 
   for (SDNode *User : Op->users()) {
@@ -143,7 +140,7 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
       for (unsigned i = 0, e = User->getNumOperands(); i != e; ++i) {
         if (User->getOperand(i) != Op)
           continue;
-        if (VT == MVT::Other || VT == MVT::Glue || !TLI->isTypeLegal(VT))
+        if (VT == MVT::Other || VT == MVT::Glue)
           continue;
         Match = false;
         if (User->isMachineOpcode()) {
@@ -157,7 +154,7 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
           if (!UseRC)
             UseRC = RC;
           else if (RC) {
-              const TargetRegisterClass *ComRC =
+            const TargetRegisterClass *ComRC =
                 TRI->getCommonSubClass(UseRC, RC);
             // If multiple uses expect disjoint register classes, we emit
             // copies in AddRegisterOperand.
@@ -174,10 +171,10 @@ void InstrEmitter::EmitCopyFromReg(SDValue Op, bool IsClone, Register SrcReg,
 
   const TargetRegisterClass *SrcRC = nullptr, *DstRC = nullptr;
   SrcRC = TRI->getMinimalPhysRegClass(SrcReg, VT);
+  // if (!SrcRC->isAllocatable())
+  //  SrcRC = TRI->getCrossCopyRegClass(SrcRC);
 
   // Figure out the register class to create for the destreg.
-  // If SrcReg is phsysical register, the corresponding register class could be
-  // non allocable, so we prefer UseRC to SrcRC
   if (VRBase) {
     DstRC = MRI->getRegClass(VRBase);
   } else if (UseRC) {



More information about the llvm-commits mailing list