[llvm] [AMDGPU] Break v2i32 and/or/xor ops into i32 pairs when followed by t… (PR #191422)
Wooseok Lee via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 13 06:23:18 PDT 2026
https://github.com/wooseoklee updated https://github.com/llvm/llvm-project/pull/191422
>From 2fcea601dca2ed8e13a6cd8e5c5e439c5bb6d2ce Mon Sep 17 00:00:00 2001
From: wooseoklee <wolee at amd.com>
Date: Thu, 9 Apr 2026 12:33:37 -0500
Subject: [PATCH 1/3] [AMDGPU] Break v2i32 and/or/xor ops into i32 pairs when
followed by the i32 op
When a divergent v2i32 AND/OR/XOR operation is consumed by the subsequent
i32 bitwise operation (through EXTRACT_VECTOR_ELT), splitting
the v2i32 op into two scalar i32 ops before instruction selection allows
the i32 ops to be naturally combined into VOP3 instructions such as
v_bitop3_b32.
Add a DAG combine `performAndOrXorv2i32Combine` that fires after DAG
op legalization and decomposes a divergent v2i32 AND/OR/XOR node into
two i32 nodes when such a consumer exists. The combine avoids any
existing patterns (and-or, or-or).
---
llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 67 +++++
llvm/test/CodeGen/AMDGPU/bitop3.ll | 323 ++++++++++++++++++++++
2 files changed, 390 insertions(+)
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 6750dfcbaac62..ffbc8e31f4feb 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -13956,6 +13956,58 @@ static uint32_t getPermuteMask(SDValue V) {
return ~0;
}
+static SDValue performAndOrXorv2i32Combine(SDNode *N, SelectionDAG &DAG) {
+ EVT VT = N->getValueType(0);
+ const unsigned Opc = N->getOpcode();
+ assert(VT == MVT::v2i32);
+ if (!N->isDivergent())
+ return SDValue();
+
+ SDValue LHS = N->getOperand(0);
+ SDValue RHS = N->getOperand(1);
+
+ // Reject v2i32 ops that would be handled by v2i32-v2i32 patterns
+ auto matchV2I32Patterns = [](const unsigned Opc,
+ const unsigned userOpc) -> bool {
+ return (Opc == ISD::AND || Opc == ISD::OR) && userOpc == ISD::OR;
+ };
+ if (matchV2I32Patterns(LHS.getOpcode(), Opc) ||
+ matchV2I32Patterns(RHS.getOpcode(), Opc))
+ return SDValue();
+
+ for (SDUse &use : N->uses()) {
+ SDNode *user = use.getUser();
+ if (user->getValueType(0) == MVT::v2i32 &&
+ matchV2I32Patterns(Opc, user->getOpcode()))
+ return SDValue();
+ }
+
+ // If the v2i32 op has a following i32 operation, break v2i32 into two i32 ops
+ // before instruction selection such that the broken i32 ops can be naturally
+ // optimized, e.g., into vop3 op
+ for (SDUse &use : N->uses()) {
+ SDNode *user = use.getUser();
+ SDNode *peek = user->getOpcode() == ISD::EXTRACT_VECTOR_ELT
+ ? user->use_begin()->getUser()
+ : user;
+ const unsigned peekOpc = peek->getOpcode();
+ EVT peekVT = peek->getValueType(0);
+ if (peekVT == MVT::i32 &&
+ (peekOpc == ISD::OR || peekOpc == ISD::AND || peekOpc == ISD::XOR)) {
+ SDLoc DL(N);
+ SmallVector<SDValue, 2> LhsValues;
+ SmallVector<SDValue, 2> RhsValues;
+ DAG.ExtractVectorElements(LHS, LhsValues, 0, 2, MVT::i32);
+ DAG.ExtractVectorElements(RHS, RhsValues, 0, 2, MVT::i32);
+ SDValue Lo = DAG.getNode(Opc, DL, MVT::i32, LhsValues[0], RhsValues[0]);
+ SDValue Hi = DAG.getNode(Opc, DL, MVT::i32, LhsValues[1], RhsValues[1]);
+ return DAG.getNode(ISD::BUILD_VECTOR, DL, MVT::v2i32, Lo, Hi);
+ }
+ }
+
+ return SDValue();
+}
+
SDValue SITargetLowering::performAndCombine(SDNode *N,
DAGCombinerInfo &DCI) const {
if (DCI.isBeforeLegalize())
@@ -14138,6 +14190,11 @@ SDValue SITargetLowering::performAndCombine(SDNode *N,
}
}
+ if (DCI.isAfterLegalizeDAG() && VT == MVT::v2i32) {
+ if (SDValue RV = performAndOrXorv2i32Combine(N, DAG))
+ return RV;
+ }
+
return SDValue();
}
@@ -14890,6 +14947,11 @@ SDValue SITargetLowering::performOrCombine(SDNode *N,
}
}
}
+
+ if (DCI.isAfterLegalizeDAG() && VT == MVT::v2i32) {
+ if (SDValue RV = performAndOrXorv2i32Combine(N, DAG))
+ return RV;
+ }
if (VT != MVT::i64 || DCI.isBeforeLegalizeOps())
return SDValue();
@@ -14985,6 +15047,11 @@ SDValue SITargetLowering::performXorCombine(SDNode *N,
return DAG.getNode(ISD::BITCAST, DL, VT, NewSelect);
}
}
+
+ if (DCI.isAfterLegalizeDAG() && VT == MVT::v2i32) {
+ if (SDValue RV = performAndOrXorv2i32Combine(N, DAG))
+ return RV;
+ }
return SDValue();
}
diff --git a/llvm/test/CodeGen/AMDGPU/bitop3.ll b/llvm/test/CodeGen/AMDGPU/bitop3.ll
index 75039e447a4d9..dcc515910f9af 100644
--- a/llvm/test/CodeGen/AMDGPU/bitop3.ll
+++ b/llvm/test/CodeGen/AMDGPU/bitop3.ll
@@ -741,6 +741,329 @@ define amdgpu_ps half @test_and_or_b16(i16 %a, i16 %b, i16 %c) {
%ret_cast = bitcast i16 %or1 to half
ret half %ret_cast
}
+
+; ========= v2i32-i32 pattern tests =========
+
+define i32 @v_v2i32_or_i32_xor(<2 x i32> %a, <2 x i32> %b) {
+; GFX950-LABEL: v_v2i32_or_i32_xor:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_or_b32_e32 v1, v1, v3
+; GFX950-NEXT: v_bitop3_b32 v0, v0, v1, v2 bitop3:0x36
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-LABEL: v_v2i32_or_i32_xor:
+; GFX1250: ; %bb.0:
+; GFX1250-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-NEXT: s_wait_kmcnt 0x0
+; GFX1250-NEXT: v_or_b32_e32 v1, v1, v3
+; GFX1250-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-NEXT: v_bitop3_b32 v0, v0, v1, v2 bitop3:0x36
+; GFX1250-NEXT: s_set_pc_i64 s[30:31]
+ %x = or disjoint <2 x i32> %a, %b
+ %x_lo = extractelement <2 x i32> %x, i64 0
+ %x_hi = extractelement <2 x i32> %x, i64 1
+ %res = xor i32 %x_lo, %x_hi
+ ret i32 %res
+}
+
+; Test if bitop3 is not created in SDAG under this pattern v2i32(and)-v2i32(or)-i32
+define i32 @v_v2i32_and_v2i32_or_i32_xor(<2 x i32> %a, <2 x i32> %b, <2 x i32> %c) {
+; GFX950-SDAG-LABEL: v_v2i32_and_v2i32_or_i32_xor:
+; GFX950-SDAG: ; %bb.0:
+; GFX950-SDAG-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT: v_and_or_b32 v1, v1, v3, v5
+; GFX950-SDAG-NEXT: v_and_or_b32 v0, v0, v2, v4
+; GFX950-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX950-SDAG-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-GISEL-LABEL: v_v2i32_and_v2i32_or_i32_xor:
+; GFX950-GISEL: ; %bb.0:
+; GFX950-GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-GISEL-NEXT: v_and_b32_e32 v0, v0, v2
+; GFX950-GISEL-NEXT: v_and_or_b32 v1, v1, v3, v5
+; GFX950-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, v4 bitop3:0x36
+; GFX950-GISEL-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-SDAG-LABEL: v_v2i32_and_v2i32_or_i32_xor:
+; GFX1250-SDAG: ; %bb.0:
+; GFX1250-SDAG-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-SDAG-NEXT: s_wait_kmcnt 0x0
+; GFX1250-SDAG-NEXT: v_and_or_b32 v1, v1, v3, v5
+; GFX1250-SDAG-NEXT: v_and_or_b32 v0, v0, v2, v4
+; GFX1250-SDAG-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX1250-SDAG-NEXT: s_set_pc_i64 s[30:31]
+;
+; GFX1250-GISEL-LABEL: v_v2i32_and_v2i32_or_i32_xor:
+; GFX1250-GISEL: ; %bb.0:
+; GFX1250-GISEL-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-GISEL-NEXT: s_wait_kmcnt 0x0
+; GFX1250-GISEL-NEXT: v_and_b32_e32 v0, v0, v2
+; GFX1250-GISEL-NEXT: v_and_or_b32 v1, v1, v3, v5
+; GFX1250-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, v4 bitop3:0x36
+; GFX1250-GISEL-NEXT: s_set_pc_i64 s[30:31]
+ %x = and <2 x i32> %a, %b
+ %y = or disjoint <2 x i32> %x, %c
+ %y_lo = extractelement <2 x i32> %y, i64 0
+ %y_hi = extractelement <2 x i32> %y, i64 1
+ %res = xor i32 %y_lo, %y_hi
+ ret i32 %res
+}
+
+; Test if bitop3 is not created in SDAG under this pattern v2i32(or)-v2i32(or)-i32
+define i32 @v_v2i32_or_v2i32_or_i32_xor(<2 x i32> %a, <2 x i32> %b, <2 x i32> %c) {
+; GFX950-SDAG-LABEL: v_v2i32_or_v2i32_or_i32_xor:
+; GFX950-SDAG: ; %bb.0:
+; GFX950-SDAG-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT: v_or3_b32 v1, v1, v3, v5
+; GFX950-SDAG-NEXT: v_or3_b32 v0, v0, v2, v4
+; GFX950-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX950-SDAG-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-GISEL-LABEL: v_v2i32_or_v2i32_or_i32_xor:
+; GFX950-GISEL: ; %bb.0:
+; GFX950-GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-GISEL-NEXT: v_or_b32_e32 v0, v0, v2
+; GFX950-GISEL-NEXT: v_or3_b32 v1, v1, v3, v5
+; GFX950-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, v4 bitop3:0x36
+; GFX950-GISEL-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-SDAG-LABEL: v_v2i32_or_v2i32_or_i32_xor:
+; GFX1250-SDAG: ; %bb.0:
+; GFX1250-SDAG-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-SDAG-NEXT: s_wait_kmcnt 0x0
+; GFX1250-SDAG-NEXT: v_or3_b32 v1, v1, v3, v5
+; GFX1250-SDAG-NEXT: v_or3_b32 v0, v0, v2, v4
+; GFX1250-SDAG-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX1250-SDAG-NEXT: s_set_pc_i64 s[30:31]
+;
+; GFX1250-GISEL-LABEL: v_v2i32_or_v2i32_or_i32_xor:
+; GFX1250-GISEL: ; %bb.0:
+; GFX1250-GISEL-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-GISEL-NEXT: s_wait_kmcnt 0x0
+; GFX1250-GISEL-NEXT: v_or_b32_e32 v0, v0, v2
+; GFX1250-GISEL-NEXT: v_or3_b32 v1, v1, v3, v5
+; GFX1250-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, v4 bitop3:0x36
+; GFX1250-GISEL-NEXT: s_set_pc_i64 s[30:31]
+ %x = or <2 x i32> %a, %b
+ %y = or disjoint <2 x i32> %x, %c
+ %y_lo = extractelement <2 x i32> %y, i64 0
+ %y_hi = extractelement <2 x i32> %y, i64 1
+ %res = xor i32 %y_lo, %y_hi
+ ret i32 %res
+}
+
+; Test if bitop3 is created under this pattern v2i32(xor)-v2i32(and)-i32
+define i32 @v_v2i32_and_v2i32_and_i32_xor(<2 x i32> %a, <2 x i32> %b, <2 x i32> %c) {
+; GFX950-LABEL: v_v2i32_and_v2i32_and_i32_xor:
+; GFX950: ; %bb.0:
+; GFX950-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-NEXT: v_xor_b32_e32 v0, v0, v2
+; GFX950-NEXT: v_bitop3_b32 v1, v1, v5, v3 bitop3:0x48
+; GFX950-NEXT: v_bitop3_b32 v0, v0, v1, v4 bitop3:0x6c
+; GFX950-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-LABEL: v_v2i32_and_v2i32_and_i32_xor:
+; GFX1250: ; %bb.0:
+; GFX1250-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-NEXT: s_wait_kmcnt 0x0
+; GFX1250-NEXT: v_xor_b32_e32 v0, v0, v2
+; GFX1250-NEXT: v_bitop3_b32 v1, v1, v5, v3 bitop3:0x48
+; GFX1250-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-NEXT: v_bitop3_b32 v0, v0, v1, v4 bitop3:0x6c
+; GFX1250-NEXT: s_set_pc_i64 s[30:31]
+ %x = xor <2 x i32> %a, %b
+ %y = and <2 x i32> %x, %c
+ %y_lo = extractelement <2 x i32> %y, i64 0
+ %y_hi = extractelement <2 x i32> %y, i64 1
+ %res = xor i32 %y_lo, %y_hi
+ ret i32 %res
+}
+
+define i32 @v_v2i32_or_i32_xor_const(<2 x i32> %a) {
+; GFX950-SDAG-LABEL: v_v2i32_or_i32_xor_const:
+; GFX950-SDAG: ; %bb.0:
+; GFX950-SDAG-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT: v_bitop3_b32 v0, v1, -16, v1 bitop3:0xc
+; GFX950-SDAG-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-GISEL-LABEL: v_v2i32_or_i32_xor_const:
+; GFX950-GISEL: ; %bb.0:
+; GFX950-GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-GISEL-NEXT: v_or_b32_e32 v0, 15, v1
+; GFX950-GISEL-NEXT: v_xor_b32_e32 v0, -1, v0
+; GFX950-GISEL-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-SDAG-LABEL: v_v2i32_or_i32_xor_const:
+; GFX1250-SDAG: ; %bb.0:
+; GFX1250-SDAG-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-SDAG-NEXT: s_wait_kmcnt 0x0
+; GFX1250-SDAG-NEXT: v_bitop3_b32 v0, v1, -16, v1 bitop3:0xc
+; GFX1250-SDAG-NEXT: s_set_pc_i64 s[30:31]
+;
+; GFX1250-GISEL-LABEL: v_v2i32_or_i32_xor_const:
+; GFX1250-GISEL: ; %bb.0:
+; GFX1250-GISEL-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-GISEL-NEXT: s_wait_kmcnt 0x0
+; GFX1250-GISEL-NEXT: v_or_b32_e32 v0, 15, v1
+; GFX1250-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-GISEL-NEXT: v_xor_b32_e32 v0, -1, v0
+; GFX1250-GISEL-NEXT: s_set_pc_i64 s[30:31]
+ %x = or <2 x i32> %a, <i32 -1, i32 15>
+ %x_lo = extractelement <2 x i32> %x, i64 0
+ %x_hi = extractelement <2 x i32> %x, i64 1
+ %res = xor i32 %x_lo, %x_hi
+ ret i32 %res
+}
+
+define i32 @v_v2i32_and_v2i32_or_i32_xor_constx2(<2 x i32> %a) {
+; GFX950-SDAG-LABEL: v_v2i32_and_v2i32_or_i32_xor_constx2:
+; GFX950-SDAG: ; %bb.0:
+; GFX950-SDAG-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT: v_and_b32_e32 v1, 15, v1
+; GFX950-SDAG-NEXT: v_and_b32_e32 v0, 4, v0
+; GFX950-SDAG-NEXT: v_or_b32_e32 v1, 7, v1
+; GFX950-SDAG-NEXT: v_or_b32_e32 v0, 8, v0
+; GFX950-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX950-SDAG-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-GISEL-LABEL: v_v2i32_and_v2i32_or_i32_xor_constx2:
+; GFX950-GISEL: ; %bb.0:
+; GFX950-GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-GISEL-NEXT: v_and_b32_e32 v0, 4, v0
+; GFX950-GISEL-NEXT: v_and_or_b32 v1, v1, 15, 7
+; GFX950-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, 8 bitop3:0x36
+; GFX950-GISEL-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-SDAG-LABEL: v_v2i32_and_v2i32_or_i32_xor_constx2:
+; GFX1250-SDAG: ; %bb.0:
+; GFX1250-SDAG-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-SDAG-NEXT: s_wait_kmcnt 0x0
+; GFX1250-SDAG-NEXT: v_and_or_b32 v1, v1, 15, 7
+; GFX1250-SDAG-NEXT: v_and_or_b32 v0, v0, 4, 8
+; GFX1250-SDAG-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX1250-SDAG-NEXT: s_set_pc_i64 s[30:31]
+;
+; GFX1250-GISEL-LABEL: v_v2i32_and_v2i32_or_i32_xor_constx2:
+; GFX1250-GISEL: ; %bb.0:
+; GFX1250-GISEL-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-GISEL-NEXT: s_wait_kmcnt 0x0
+; GFX1250-GISEL-NEXT: v_and_b32_e32 v0, 4, v0
+; GFX1250-GISEL-NEXT: v_and_or_b32 v1, v1, 15, 7
+; GFX1250-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, 8 bitop3:0x36
+; GFX1250-GISEL-NEXT: s_set_pc_i64 s[30:31]
+ %x = and <2 x i32> %a, <i32 4, i32 15>
+ %y = or <2 x i32> %x, <i32 8, i32 7>
+ %y_lo = extractelement <2 x i32> %y, i64 0
+ %y_hi = extractelement <2 x i32> %y, i64 1
+ %res = xor i32 %y_lo, %y_hi
+ ret i32 %res
+}
+
+define i32 @v_not_and_not_and(<2 x i32> %a, <2 x i32> %b) {
+; GFX950-SDAG-LABEL: v_not_and_not_and:
+; GFX950-SDAG: ; %bb.0:
+; GFX950-SDAG-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT: v_bitop3_b32 v1, v1, v3, v1 bitop3:3
+; GFX950-SDAG-NEXT: v_bitop3_b32 v0, v0, v1, v2 bitop3:0xc9
+; GFX950-SDAG-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-GISEL-LABEL: v_not_and_not_and:
+; GFX950-GISEL: ; %bb.0:
+; GFX950-GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-GISEL-NEXT: v_not_b32_e32 v3, v3
+; GFX950-GISEL-NEXT: v_bfi_b32 v1, v1, 0, v3
+; GFX950-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, v2 bitop3:0xc9
+; GFX950-GISEL-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-SDAG-LABEL: v_not_and_not_and:
+; GFX1250-SDAG: ; %bb.0:
+; GFX1250-SDAG-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-SDAG-NEXT: s_wait_kmcnt 0x0
+; GFX1250-SDAG-NEXT: v_bitop3_b32 v1, v1, v3, v1 bitop3:3
+; GFX1250-SDAG-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-SDAG-NEXT: v_bitop3_b32 v0, v0, v1, v2 bitop3:0xc9
+; GFX1250-SDAG-NEXT: s_set_pc_i64 s[30:31]
+;
+; GFX1250-GISEL-LABEL: v_not_and_not_and:
+; GFX1250-GISEL: ; %bb.0:
+; GFX1250-GISEL-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-GISEL-NEXT: s_wait_kmcnt 0x0
+; GFX1250-GISEL-NEXT: v_not_b32_e32 v3, v3
+; GFX1250-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX1250-GISEL-NEXT: v_bfi_b32 v1, v1, 0, v3
+; GFX1250-GISEL-NEXT: v_bitop3_b32 v0, v0, v1, v2 bitop3:0xc9
+; GFX1250-GISEL-NEXT: s_set_pc_i64 s[30:31]
+ %nota = xor <2 x i32> %a, <i32 -1, i32 -1>
+ %notb = xor <2 x i32> %b, <i32 -1, i32 -1>
+ %and1 = and <2 x i32> %nota, %notb
+ %and1_lo = extractelement <2 x i32> %and1, i64 0
+ %and1_hi = extractelement <2 x i32> %and1, i64 1
+ %res = xor i32 %and1_lo, %and1_hi
+ ret i32 %res
+}
+
+define i32 @v_not_or_and_or(<2 x i32> %a, <2 x i32> %b) {
+; GFX950-SDAG-LABEL: v_not_or_and_or:
+; GFX950-SDAG: ; %bb.0:
+; GFX950-SDAG-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT: v_xor_b32_e32 v1, -1, v1
+; GFX950-SDAG-NEXT: v_xor_b32_e32 v0, -1, v0
+; GFX950-SDAG-NEXT: v_and_or_b32 v1, v3, 8, v1
+; GFX950-SDAG-NEXT: v_and_or_b32 v0, v2, 16, v0
+; GFX950-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX950-SDAG-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX950-GISEL-LABEL: v_not_or_and_or:
+; GFX950-GISEL: ; %bb.0:
+; GFX950-GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-GISEL-NEXT: v_and_b32_e32 v2, 16, v2
+; GFX950-GISEL-NEXT: v_and_b32_e32 v3, 8, v3
+; GFX950-GISEL-NEXT: v_bfi_b32 v0, v0, v2, -1
+; GFX950-GISEL-NEXT: v_bfi_b32 v1, v1, v3, -1
+; GFX950-GISEL-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX950-GISEL-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX1250-SDAG-LABEL: v_not_or_and_or:
+; GFX1250-SDAG: ; %bb.0:
+; GFX1250-SDAG-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-SDAG-NEXT: s_wait_kmcnt 0x0
+; GFX1250-SDAG-NEXT: v_xor_b32_e32 v1, -1, v1
+; GFX1250-SDAG-NEXT: v_xor_b32_e32 v0, -1, v0
+; GFX1250-SDAG-NEXT: s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1250-SDAG-NEXT: v_and_or_b32 v1, v3, 8, v1
+; GFX1250-SDAG-NEXT: v_and_or_b32 v0, v2, 16, v0
+; GFX1250-SDAG-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-SDAG-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX1250-SDAG-NEXT: s_set_pc_i64 s[30:31]
+;
+; GFX1250-GISEL-LABEL: v_not_or_and_or:
+; GFX1250-GISEL: ; %bb.0:
+; GFX1250-GISEL-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX1250-GISEL-NEXT: s_wait_kmcnt 0x0
+; GFX1250-GISEL-NEXT: v_and_b32_e32 v2, 16, v2
+; GFX1250-GISEL-NEXT: v_and_b32_e32 v3, 8, v3
+; GFX1250-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1250-GISEL-NEXT: v_bfi_b32 v0, v0, v2, -1
+; GFX1250-GISEL-NEXT: v_bfi_b32 v1, v1, v3, -1
+; GFX1250-GISEL-NEXT: s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-GISEL-NEXT: v_xor_b32_e32 v0, v0, v1
+; GFX1250-GISEL-NEXT: s_set_pc_i64 s[30:31]
+ %nota = xor <2 x i32> %a, <i32 -1, i32 -1>
+ %andb = and <2 x i32> %b, <i32 16, i32 8>
+ %or = or <2 x i32> %nota, %andb
+ %or_lo = extractelement <2 x i32> %or, i64 0
+ %or_hi = extractelement <2 x i32> %or, i64 1
+ %res = xor i32 %or_lo, %or_hi
+ ret i32 %res
+}
+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; GCN: {{.*}}
; GFX1250-FAKE16: {{.*}}
>From f57956bc9d0a83fa9e73336c52f7605f070e5977 Mon Sep 17 00:00:00 2001
From: wooseoklee <wolee at amd.com>
Date: Fri, 10 Apr 2026 09:14:41 -0500
Subject: [PATCH 2/3] clang-format fix
---
llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index ffbc8e31f4feb..05117eb5a1b8a 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -14947,7 +14947,7 @@ SDValue SITargetLowering::performOrCombine(SDNode *N,
}
}
}
-
+
if (DCI.isAfterLegalizeDAG() && VT == MVT::v2i32) {
if (SDValue RV = performAndOrXorv2i32Combine(N, DAG))
return RV;
@@ -15047,7 +15047,7 @@ SDValue SITargetLowering::performXorCombine(SDNode *N,
return DAG.getNode(ISD::BITCAST, DL, VT, NewSelect);
}
}
-
+
if (DCI.isAfterLegalizeDAG() && VT == MVT::v2i32) {
if (SDValue RV = performAndOrXorv2i32Combine(N, DAG))
return RV;
>From 59012f3508c1d1bcbf32c70ec61e843b71f80ef0 Mon Sep 17 00:00:00 2001
From: wooseoklee <wolee at amd.com>
Date: Mon, 13 Apr 2026 08:22:53 -0500
Subject: [PATCH 3/3] Merge two use loop to minimize the use traversal
---
llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 37 +++++++++++------------
1 file changed, 18 insertions(+), 19 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 05117eb5a1b8a..d776ebff1f700 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -13975,37 +13975,36 @@ static SDValue performAndOrXorv2i32Combine(SDNode *N, SelectionDAG &DAG) {
matchV2I32Patterns(RHS.getOpcode(), Opc))
return SDValue();
+ bool foundi32LogicalOp = false;
for (SDUse &use : N->uses()) {
SDNode *user = use.getUser();
if (user->getValueType(0) == MVT::v2i32 &&
matchV2I32Patterns(Opc, user->getOpcode()))
return SDValue();
- }
- // If the v2i32 op has a following i32 operation, break v2i32 into two i32 ops
- // before instruction selection such that the broken i32 ops can be naturally
- // optimized, e.g., into vop3 op
- for (SDUse &use : N->uses()) {
- SDNode *user = use.getUser();
SDNode *peek = user->getOpcode() == ISD::EXTRACT_VECTOR_ELT
? user->use_begin()->getUser()
: user;
const unsigned peekOpc = peek->getOpcode();
- EVT peekVT = peek->getValueType(0);
- if (peekVT == MVT::i32 &&
- (peekOpc == ISD::OR || peekOpc == ISD::AND || peekOpc == ISD::XOR)) {
- SDLoc DL(N);
- SmallVector<SDValue, 2> LhsValues;
- SmallVector<SDValue, 2> RhsValues;
- DAG.ExtractVectorElements(LHS, LhsValues, 0, 2, MVT::i32);
- DAG.ExtractVectorElements(RHS, RhsValues, 0, 2, MVT::i32);
- SDValue Lo = DAG.getNode(Opc, DL, MVT::i32, LhsValues[0], RhsValues[0]);
- SDValue Hi = DAG.getNode(Opc, DL, MVT::i32, LhsValues[1], RhsValues[1]);
- return DAG.getNode(ISD::BUILD_VECTOR, DL, MVT::v2i32, Lo, Hi);
- }
+ if (peek->getValueType(0) == MVT::i32 &&
+ (peekOpc == ISD::OR || peekOpc == ISD::AND || peekOpc == ISD::XOR))
+ foundi32LogicalOp = true;
}
- return SDValue();
+ if (!foundi32LogicalOp)
+ return SDValue();
+
+ // If the v2i32 op has a following i32 operation, break v2i32 into two i32
+ // ops before instruction selection such that the broken i32 ops can be
+ // naturally optimized, e.g., into vop3 op.
+ SDLoc DL(N);
+ SmallVector<SDValue, 2> LhsValues;
+ SmallVector<SDValue, 2> RhsValues;
+ DAG.ExtractVectorElements(LHS, LhsValues, 0, 2, MVT::i32);
+ DAG.ExtractVectorElements(RHS, RhsValues, 0, 2, MVT::i32);
+ SDValue Lo = DAG.getNode(Opc, DL, MVT::i32, LhsValues[0], RhsValues[0]);
+ SDValue Hi = DAG.getNode(Opc, DL, MVT::i32, LhsValues[1], RhsValues[1]);
+ return DAG.getNode(ISD::BUILD_VECTOR, DL, MVT::v2i32, Lo, Hi);
}
SDValue SITargetLowering::performAndCombine(SDNode *N,
More information about the llvm-commits
mailing list