[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