[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