[llvm] [AMDGPU][X86][DAG] Avoid duplicate BinOp result from narrowing insert-extract sub-vector (PR #201056)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 5 16:57:40 PDT 2026


https://github.com/Shoreshen updated https://github.com/llvm/llvm-project/pull/201056

>From 8a3d5809ebc71ec3bff13d7234e8fa8fc97e576b Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Tue, 2 Jun 2026 16:17:22 +0800
Subject: [PATCH 01/15] use target specific hook to block narrowing
 insert-extract subvector

---
 llvm/include/llvm/CodeGen/TargetLowering.h    |  7 ++
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp |  2 +
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp     | 15 +++-
 llvm/lib/Target/AMDGPU/SIISelLowering.h       |  3 +-
 llvm/test/CodeGen/AMDGPU/fmax_legacy.f16.ll   |  2 +-
 llvm/test/CodeGen/AMDGPU/fmin_legacy.f16.ll   |  2 +-
 llvm/test/CodeGen/AMDGPU/llvm.maximum.f16.ll  |  8 +-
 llvm/test/CodeGen/AMDGPU/llvm.minimum.f16.ll  |  8 +-
 llvm/test/CodeGen/AMDGPU/maximumnum.ll        | 84 +++++++++----------
 llvm/test/CodeGen/AMDGPU/minimumnum.ll        | 84 +++++++++----------
 llvm/test/CodeGen/AMDGPU/saddsat.ll           |  2 +-
 llvm/test/CodeGen/AMDGPU/ssubsat.ll           |  2 +-
 llvm/test/CodeGen/AMDGPU/uaddsat.ll           |  2 +-
 llvm/test/CodeGen/AMDGPU/usubsat.ll           |  2 +-
 14 files changed, 121 insertions(+), 102 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 82c47cce0f522..a57fd2ede44ea 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -3527,6 +3527,13 @@ class LLVM_ABI TargetLoweringBase {
     return false;
   }
 
+  /// Return true if it is profitable to narrow a vector binop feeding an
+  /// extract_subvector into a binop of the extracted vector type.
+  virtual bool shouldNarrowExtractedVectorBinOp(EVT SrcVT, EVT SubVT,
+                                                unsigned Index) const {
+    return true;
+  }
+
   /// Return true if extraction of a scalar element from the given vector type
   /// at the given index is cheap. For example, if scalar operations occur on
   /// the same register file as vector operations, then an extract element may
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 0c9820fb64de9..c4c12fbf5dde6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27192,6 +27192,8 @@ static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
     return SDValue();
 
   EVT VecVT = BinOp.getValueType();
+  if (!TLI.shouldNarrowExtractedVectorBinOp(VecVT, SubVT, Index))
+    return SDValue();
   SDValue Bop0 = BinOp.getOperand(0), Bop1 = BinOp.getOperand(1);
   if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType())
     return SDValue();
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 86f2479490c29..b735d00be58e7 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -892,7 +892,7 @@ SITargetLowering::SITargetLowering(const TargetMachine &TM,
       for (MVT VT : {MVT::v4bf16, MVT::v8bf16, MVT::v16bf16, MVT::v32bf16})
         // Split vector operations.
         setOperationAction({ISD::FADD, ISD::FMUL, ISD::FMA, ISD::FCANONICALIZE,
-                            ISD::FNEG, ISD::FABS},
+                            ISD::FNEG, ISD::FABS, ISD::FMAXNUM, ISD::FMINNUM},
                            VT, Custom);
     }
 
@@ -2354,6 +2354,19 @@ bool SITargetLowering::shouldConvertConstantLoadToIntImm(const APInt &Imm,
   return true;
 }
 
+bool SITargetLowering::shouldNarrowExtractedVectorBinOp(EVT SrcVT, EVT SubVT,
+                                                        unsigned Index) const {
+  unsigned ScalarBits = SubVT.getScalarSizeInBits();
+  unsigned OffsetBits = Index * ScalarBits;
+  unsigned SubvectorBits = SubVT.getSizeInBits();
+
+  // If the extracted subvector starts on a dword boundary and has a dword
+  // sized payload, keeping the original wide result is preferable because the
+  // subvector can be referenced directly through subregisters. Otherwise,
+  // narrowing may avoid element extraction or reconstruction.
+  return OffsetBits % 32 != 0 || SubvectorBits % 32 != 0;
+}
+
 bool SITargetLowering::isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
                                                unsigned Index) const {
   if (!isOperationLegalOrCustom(ISD::EXTRACT_SUBVECTOR, ResVT))
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.h b/llvm/lib/Target/AMDGPU/SIISelLowering.h
index 95ff5bba7cfff..154faefb37ae1 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.h
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.h
@@ -399,7 +399,8 @@ class SITargetLowering final : public AMDGPUTargetLowering {
 
   bool shouldConvertConstantLoadToIntImm(const APInt &Imm,
                                         Type *Ty) const override;
-
+  bool shouldNarrowExtractedVectorBinOp(EVT SrcVT, EVT SubVT,
+                                        unsigned Index) const override;
   bool isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
                                unsigned Index) const override;
   bool isExtractVecEltCheap(EVT VT, unsigned Index) const override;
diff --git a/llvm/test/CodeGen/AMDGPU/fmax_legacy.f16.ll b/llvm/test/CodeGen/AMDGPU/fmax_legacy.f16.ll
index f980288865b8b..8a0cb0d6de739 100644
--- a/llvm/test/CodeGen/AMDGPU/fmax_legacy.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fmax_legacy.f16.ll
@@ -289,8 +289,8 @@ define <3 x half> @test_fmax_legacy_ugt_v3f16_fast(<3 x half> %a, <3 x half> %b)
 ; GFX9-LABEL: test_fmax_legacy_ugt_v3f16_fast:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
 ; GFX9-NEXT:    v_pk_max_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; VI-LABEL: test_fmax_legacy_ugt_v3f16_fast:
diff --git a/llvm/test/CodeGen/AMDGPU/fmin_legacy.f16.ll b/llvm/test/CodeGen/AMDGPU/fmin_legacy.f16.ll
index 41b7b7fe6b21a..04d9bfd4e9192 100644
--- a/llvm/test/CodeGen/AMDGPU/fmin_legacy.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fmin_legacy.f16.ll
@@ -290,8 +290,8 @@ define <3 x half> @test_fmin_legacy_ule_v3f16_fast(<3 x half> %a, <3 x half> %b)
 ; GFX9-LABEL: test_fmin_legacy_ule_v3f16_fast:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
 ; GFX9-NEXT:    v_pk_min_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; VI-LABEL: test_fmin_legacy_ule_v3f16_fast:
diff --git a/llvm/test/CodeGen/AMDGPU/llvm.maximum.f16.ll b/llvm/test/CodeGen/AMDGPU/llvm.maximum.f16.ll
index a60741905bdbd..ab5605d6d89db 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.maximum.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.maximum.f16.ll
@@ -1310,8 +1310,8 @@ define <3 x half> @v_maximum_v3f16(<3 x half> %src0, <3 x half> %src1) {
 ; GFX950-LABEL: v_maximum_v3f16:
 ; GFX950:       ; %bb.0:
 ; GFX950-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX950-NEXT:    v_pk_maximum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    v_pk_maximum3_f16 v0, v0, v2, v2
+; GFX950-NEXT:    v_pk_maximum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_maximum_v3f16:
@@ -1419,8 +1419,8 @@ define <3 x half> @v_maximum_v3f16__nnan(<3 x half> %src0, <3 x half> %src1) {
 ; GFX9-LABEL: v_maximum_v3f16__nnan:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
 ; GFX9-NEXT:    v_pk_max_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_maximum_v3f16__nnan:
@@ -1524,8 +1524,8 @@ define <3 x half> @v_maximum_v3f16__nsz(<3 x half> %src0, <3 x half> %src1) {
 ; GFX950-LABEL: v_maximum_v3f16__nsz:
 ; GFX950:       ; %bb.0:
 ; GFX950-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX950-NEXT:    v_pk_maximum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    v_pk_maximum3_f16 v0, v0, v2, v2
+; GFX950-NEXT:    v_pk_maximum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_maximum_v3f16__nsz:
@@ -1633,8 +1633,8 @@ define <3 x half> @v_maximum_v3f16__nnan_nsz(<3 x half> %src0, <3 x half> %src1)
 ; GFX9-LABEL: v_maximum_v3f16__nnan_nsz:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
 ; GFX9-NEXT:    v_pk_max_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_maximum_v3f16__nnan_nsz:
diff --git a/llvm/test/CodeGen/AMDGPU/llvm.minimum.f16.ll b/llvm/test/CodeGen/AMDGPU/llvm.minimum.f16.ll
index cd01dfc2d3dc8..a262181921f15 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.minimum.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.minimum.f16.ll
@@ -1092,8 +1092,8 @@ define <3 x half> @v_minimum_v3f16(<3 x half> %src0, <3 x half> %src1) {
 ; GFX950-LABEL: v_minimum_v3f16:
 ; GFX950:       ; %bb.0:
 ; GFX950-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX950-NEXT:    v_pk_minimum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    v_pk_minimum3_f16 v0, v0, v2, v2
+; GFX950-NEXT:    v_pk_minimum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_minimum_v3f16:
@@ -1180,8 +1180,8 @@ define <3 x half> @v_minimum_v3f16__nnan(<3 x half> %src0, <3 x half> %src1) {
 ; GFX9-LABEL: v_minimum_v3f16__nnan:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
 ; GFX9-NEXT:    v_pk_min_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_minimum_v3f16__nnan:
@@ -1257,8 +1257,8 @@ define <3 x half> @v_minimum_v3f16__nsz(<3 x half> %src0, <3 x half> %src1) {
 ; GFX950-LABEL: v_minimum_v3f16__nsz:
 ; GFX950:       ; %bb.0:
 ; GFX950-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX950-NEXT:    v_pk_minimum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    v_pk_minimum3_f16 v0, v0, v2, v2
+; GFX950-NEXT:    v_pk_minimum3_f16 v1, v1, v3, v3
 ; GFX950-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_minimum_v3f16__nsz:
@@ -1345,8 +1345,8 @@ define <3 x half> @v_minimum_v3f16__nnan_nsz(<3 x half> %src0, <3 x half> %src1)
 ; GFX9-LABEL: v_minimum_v3f16__nnan_nsz:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
 ; GFX9-NEXT:    v_pk_min_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_minimum_v3f16__nnan_nsz:
diff --git a/llvm/test/CodeGen/AMDGPU/maximumnum.ll b/llvm/test/CodeGen/AMDGPU/maximumnum.ll
index c0f80363e6850..4ba8ff42390ac 100644
--- a/llvm/test/CodeGen/AMDGPU/maximumnum.ll
+++ b/llvm/test/CodeGen/AMDGPU/maximumnum.ll
@@ -4301,16 +4301,16 @@ define <3 x half> @v_maximumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX8-GISEL-NEXT:    v_or_b32_e32 v0, v4, v0
 ; GFX8-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
-; GFX9-SDAG-LABEL: v_maximumnum_v3f16:
-; GFX9-SDAG:       ; %bb.0:
-; GFX9-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v1, v1, v3
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v0, v0, v2
-; GFX9-SDAG-NEXT:    s_setpc_b64 s[30:31]
+; GFX900-SDAG-LABEL: v_maximumnum_v3f16:
+; GFX900-SDAG:       ; %bb.0:
+; GFX900-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v0, v0, v2
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v2, v3, v3
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v1, v1, v2
+; GFX900-SDAG-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX900-GISEL-LABEL: v_maximumnum_v3f16:
 ; GFX900-GISEL:       ; %bb.0:
@@ -4323,6 +4323,18 @@ define <3 x half> @v_maximumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX900-GISEL-NEXT:    v_pk_max_f16 v1, v1, v2
 ; GFX900-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
+; GFX950-SDAG-LABEL: v_maximumnum_v3f16:
+; GFX950-SDAG:       ; %bb.0:
+; GFX950-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v0, v0, v2
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v2, v3, v3
+; GFX950-SDAG-NEXT:    s_nop 0
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v1, v1, v2
+; GFX950-SDAG-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX950-GISEL-LABEL: v_maximumnum_v3f16:
 ; GFX950-GISEL:       ; %bb.0:
 ; GFX950-GISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -4338,9 +4350,9 @@ define <3 x half> @v_maximumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX10-SDAG-LABEL: v_maximumnum_v3f16:
 ; GFX10-SDAG:       ; %bb.0:
 ; GFX10-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX10-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v0, v0, v2
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v1, v1, v3
@@ -4360,11 +4372,11 @@ define <3 x half> @v_maximumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX11-SDAG-LABEL: v_maximumnum_v3f16:
 ; GFX11-SDAG:       ; %bb.0:
 ; GFX11-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX11-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
-; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v0, v0, v2
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v1, v1, v3
 ; GFX11-SDAG-NEXT:    s_setpc_b64 s[30:31]
@@ -4384,11 +4396,11 @@ define <3 x half> @v_maximumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX1170-SDAG-LABEL: v_maximumnum_v3f16:
 ; GFX1170-SDAG:       ; %bb.0:
 ; GFX1170-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v2, v2, v2
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v0
+; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v1
-; GFX1170-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1170-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v2
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v3
 ; GFX1170-SDAG-NEXT:    s_setpc_b64 s[30:31]
@@ -4412,11 +4424,11 @@ define <3 x half> @v_maximumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX12-SDAG-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-SDAG-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-SDAG-NEXT:    s_wait_kmcnt 0x0
-; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v2, v2, v2
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v0
+; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v1
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX12-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v2
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v3
 ; GFX12-SDAG-NEXT:    s_setpc_b64 s[30:31]
@@ -4501,19 +4513,12 @@ define <3 x half> @v_maximumnum_v3f16_nnan(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX8-GISEL-NEXT:    v_or_b32_e32 v0, v4, v0
 ; GFX8-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
-; GFX9-SDAG-LABEL: v_maximumnum_v3f16_nnan:
-; GFX9-SDAG:       ; %bb.0:
-; GFX9-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v1, v1, v3
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v0, v0, v2
-; GFX9-SDAG-NEXT:    s_setpc_b64 s[30:31]
-;
-; GFX9-GISEL-LABEL: v_maximumnum_v3f16_nnan:
-; GFX9-GISEL:       ; %bb.0:
-; GFX9-GISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-GISEL-NEXT:    v_pk_max_f16 v0, v0, v2
-; GFX9-GISEL-NEXT:    v_pk_max_f16 v1, v1, v3
-; GFX9-GISEL-NEXT:    s_setpc_b64 s[30:31]
+; GFX9-LABEL: v_maximumnum_v3f16_nnan:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_pk_max_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_maximumnum_v3f16_nnan:
 ; GFX10:       ; %bb.0:
@@ -10079,19 +10084,12 @@ define <3 x half> @v_maximumnum_v3f16_nnan_no_ieee(<3 x half> %x, <3 x half> %y)
 ; GFX8-GISEL-NEXT:    v_or_b32_e32 v0, v4, v0
 ; GFX8-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
-; GFX9-SDAG-LABEL: v_maximumnum_v3f16_nnan_no_ieee:
-; GFX9-SDAG:       ; %bb.0:
-; GFX9-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v1, v1, v3
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v0, v0, v2
-; GFX9-SDAG-NEXT:    s_setpc_b64 s[30:31]
-;
-; GFX9-GISEL-LABEL: v_maximumnum_v3f16_nnan_no_ieee:
-; GFX9-GISEL:       ; %bb.0:
-; GFX9-GISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-GISEL-NEXT:    v_pk_max_f16 v0, v0, v2
-; GFX9-GISEL-NEXT:    v_pk_max_f16 v1, v1, v3
-; GFX9-GISEL-NEXT:    s_setpc_b64 s[30:31]
+; GFX9-LABEL: v_maximumnum_v3f16_nnan_no_ieee:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_pk_max_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_max_f16 v1, v1, v3
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_maximumnum_v3f16_nnan_no_ieee:
 ; GFX10:       ; %bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/minimumnum.ll b/llvm/test/CodeGen/AMDGPU/minimumnum.ll
index 18ea5e2dd0e6c..187e8b26d4394 100644
--- a/llvm/test/CodeGen/AMDGPU/minimumnum.ll
+++ b/llvm/test/CodeGen/AMDGPU/minimumnum.ll
@@ -4091,16 +4091,16 @@ define <3 x half> @v_minimumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX8-GISEL-NEXT:    v_or_b32_e32 v0, v4, v0
 ; GFX8-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
-; GFX9-SDAG-LABEL: v_minimumnum_v3f16:
-; GFX9-SDAG:       ; %bb.0:
-; GFX9-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
-; GFX9-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
-; GFX9-SDAG-NEXT:    v_pk_min_f16 v1, v1, v3
-; GFX9-SDAG-NEXT:    v_pk_min_f16 v0, v0, v2
-; GFX9-SDAG-NEXT:    s_setpc_b64 s[30:31]
+; GFX900-SDAG-LABEL: v_minimumnum_v3f16:
+; GFX900-SDAG:       ; %bb.0:
+; GFX900-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX900-SDAG-NEXT:    v_pk_min_f16 v0, v0, v2
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v2, v3, v3
+; GFX900-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
+; GFX900-SDAG-NEXT:    v_pk_min_f16 v1, v1, v2
+; GFX900-SDAG-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX900-GISEL-LABEL: v_minimumnum_v3f16:
 ; GFX900-GISEL:       ; %bb.0:
@@ -4113,6 +4113,18 @@ define <3 x half> @v_minimumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX900-GISEL-NEXT:    v_pk_min_f16 v1, v1, v2
 ; GFX900-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
+; GFX950-SDAG-LABEL: v_minimumnum_v3f16:
+; GFX950-SDAG:       ; %bb.0:
+; GFX950-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
+; GFX950-SDAG-NEXT:    v_pk_min_f16 v0, v0, v2
+; GFX950-SDAG-NEXT:    v_pk_max_f16 v2, v3, v3
+; GFX950-SDAG-NEXT:    s_nop 0
+; GFX950-SDAG-NEXT:    v_pk_min_f16 v1, v1, v2
+; GFX950-SDAG-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX950-GISEL-LABEL: v_minimumnum_v3f16:
 ; GFX950-GISEL:       ; %bb.0:
 ; GFX950-GISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
@@ -4128,9 +4140,9 @@ define <3 x half> @v_minimumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX10-SDAG-LABEL: v_minimumnum_v3f16:
 ; GFX10-SDAG:       ; %bb.0:
 ; GFX10-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX10-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX10-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
 ; GFX10-SDAG-NEXT:    v_pk_min_f16 v0, v0, v2
 ; GFX10-SDAG-NEXT:    v_pk_min_f16 v1, v1, v3
@@ -4150,11 +4162,11 @@ define <3 x half> @v_minimumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX11-SDAG-LABEL: v_minimumnum_v3f16:
 ; GFX11-SDAG:       ; %bb.0:
 ; GFX11-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v2, v2, v2
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v0, v0, v0
+; GFX11-SDAG-NEXT:    v_pk_max_f16 v3, v3, v3
 ; GFX11-SDAG-NEXT:    v_pk_max_f16 v1, v1, v1
-; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX11-SDAG-NEXT:    v_pk_min_f16 v0, v0, v2
 ; GFX11-SDAG-NEXT:    v_pk_min_f16 v1, v1, v3
 ; GFX11-SDAG-NEXT:    s_setpc_b64 s[30:31]
@@ -4174,11 +4186,11 @@ define <3 x half> @v_minimumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX1170-SDAG-LABEL: v_minimumnum_v3f16:
 ; GFX1170-SDAG:       ; %bb.0:
 ; GFX1170-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v2, v2, v2
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v0
+; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1170-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v1
-; GFX1170-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1170-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX1170-SDAG-NEXT:    v_pk_min_num_f16 v0, v0, v2
 ; GFX1170-SDAG-NEXT:    v_pk_min_num_f16 v1, v1, v3
 ; GFX1170-SDAG-NEXT:    s_setpc_b64 s[30:31]
@@ -4202,11 +4214,11 @@ define <3 x half> @v_minimumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX12-SDAG-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-SDAG-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-SDAG-NEXT:    s_wait_kmcnt 0x0
-; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v2, v2, v2
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v0
+; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX12-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v1
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX12-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX12-SDAG-NEXT:    v_pk_min_num_f16 v0, v0, v2
 ; GFX12-SDAG-NEXT:    v_pk_min_num_f16 v1, v1, v3
 ; GFX12-SDAG-NEXT:    s_setpc_b64 s[30:31]
@@ -4291,19 +4303,12 @@ define <3 x half> @v_minimumnum_v3f16_nnan(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX8-GISEL-NEXT:    v_or_b32_e32 v0, v4, v0
 ; GFX8-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
-; GFX9-SDAG-LABEL: v_minimumnum_v3f16_nnan:
-; GFX9-SDAG:       ; %bb.0:
-; GFX9-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-SDAG-NEXT:    v_pk_min_f16 v1, v1, v3
-; GFX9-SDAG-NEXT:    v_pk_min_f16 v0, v0, v2
-; GFX9-SDAG-NEXT:    s_setpc_b64 s[30:31]
-;
-; GFX9-GISEL-LABEL: v_minimumnum_v3f16_nnan:
-; GFX9-GISEL:       ; %bb.0:
-; GFX9-GISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-GISEL-NEXT:    v_pk_min_f16 v0, v0, v2
-; GFX9-GISEL-NEXT:    v_pk_min_f16 v1, v1, v3
-; GFX9-GISEL-NEXT:    s_setpc_b64 s[30:31]
+; GFX9-LABEL: v_minimumnum_v3f16_nnan:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_pk_min_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_minimumnum_v3f16_nnan:
 ; GFX10:       ; %bb.0:
@@ -9869,19 +9874,12 @@ define <3 x half> @v_minimumnum_v3f16_nnan_no_ieee(<3 x half> %x, <3 x half> %y)
 ; GFX8-GISEL-NEXT:    v_or_b32_e32 v0, v4, v0
 ; GFX8-GISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
-; GFX9-SDAG-LABEL: v_minimumnum_v3f16_nnan_no_ieee:
-; GFX9-SDAG:       ; %bb.0:
-; GFX9-SDAG-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-SDAG-NEXT:    v_pk_min_f16 v1, v1, v3
-; GFX9-SDAG-NEXT:    v_pk_min_f16 v0, v0, v2
-; GFX9-SDAG-NEXT:    s_setpc_b64 s[30:31]
-;
-; GFX9-GISEL-LABEL: v_minimumnum_v3f16_nnan_no_ieee:
-; GFX9-GISEL:       ; %bb.0:
-; GFX9-GISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-GISEL-NEXT:    v_pk_min_f16 v0, v0, v2
-; GFX9-GISEL-NEXT:    v_pk_min_f16 v1, v1, v3
-; GFX9-GISEL-NEXT:    s_setpc_b64 s[30:31]
+; GFX9-LABEL: v_minimumnum_v3f16_nnan_no_ieee:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_pk_min_f16 v0, v0, v2
+; GFX9-NEXT:    v_pk_min_f16 v1, v1, v3
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_minimumnum_v3f16_nnan_no_ieee:
 ; GFX10:       ; %bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/saddsat.ll b/llvm/test/CodeGen/AMDGPU/saddsat.ll
index 5d5f3be9ce9fd..b8e44f26b3a0f 100644
--- a/llvm/test/CodeGen/AMDGPU/saddsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/saddsat.ll
@@ -268,8 +268,8 @@ define <3 x i16> @v_saddsat_v3i16(<3 x i16> %lhs, <3 x i16> %rhs) {
 ; GFX9-LABEL: v_saddsat_v3i16:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_add_i16 v1, v1, v3 clamp
 ; GFX9-NEXT:    v_pk_add_i16 v0, v0, v2 clamp
+; GFX9-NEXT:    v_pk_add_i16 v1, v1, v3 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10PLUS-LABEL: v_saddsat_v3i16:
diff --git a/llvm/test/CodeGen/AMDGPU/ssubsat.ll b/llvm/test/CodeGen/AMDGPU/ssubsat.ll
index d65cb0a3432f2..7a9a340c3f40b 100644
--- a/llvm/test/CodeGen/AMDGPU/ssubsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/ssubsat.ll
@@ -268,8 +268,8 @@ define <3 x i16> @v_ssubsat_v3i16(<3 x i16> %lhs, <3 x i16> %rhs) {
 ; GFX9-LABEL: v_ssubsat_v3i16:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_sub_i16 v1, v1, v3 clamp
 ; GFX9-NEXT:    v_pk_sub_i16 v0, v0, v2 clamp
+; GFX9-NEXT:    v_pk_sub_i16 v1, v1, v3 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10PLUS-LABEL: v_ssubsat_v3i16:
diff --git a/llvm/test/CodeGen/AMDGPU/uaddsat.ll b/llvm/test/CodeGen/AMDGPU/uaddsat.ll
index 63ab0a3bde0e6..484c69e3c7e00 100644
--- a/llvm/test/CodeGen/AMDGPU/uaddsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/uaddsat.ll
@@ -218,8 +218,8 @@ define <3 x i16> @v_uaddsat_v3i16(<3 x i16> %lhs, <3 x i16> %rhs) {
 ; GFX9-LABEL: v_uaddsat_v3i16:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_add_u16 v1, v1, v3 clamp
 ; GFX9-NEXT:    v_pk_add_u16 v0, v0, v2 clamp
+; GFX9-NEXT:    v_pk_add_u16 v1, v1, v3 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: v_uaddsat_v3i16:
diff --git a/llvm/test/CodeGen/AMDGPU/usubsat.ll b/llvm/test/CodeGen/AMDGPU/usubsat.ll
index 97bb4112ecdcd..549e39bd91739 100644
--- a/llvm/test/CodeGen/AMDGPU/usubsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/usubsat.ll
@@ -339,8 +339,8 @@ define <3 x i16> @v_usubsat_v3i16(<3 x i16> %lhs, <3 x i16> %rhs) {
 ; GFX9-LABEL: v_usubsat_v3i16:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_pk_sub_u16 v1, v1, v3 clamp
 ; GFX9-NEXT:    v_pk_sub_u16 v0, v0, v2 clamp
+; GFX9-NEXT:    v_pk_sub_u16 v1, v1, v3 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10PLUS-LABEL: v_usubsat_v3i16:

>From 7b79a7fa54fcbe053e1e8d11d38391e32772be6e Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Wed, 3 Jun 2026 10:59:15 +0800
Subject: [PATCH 02/15] using isNarrowingProfitable

---
 llvm/include/llvm/CodeGen/TargetLowering.h    |  7 -------
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 18 ++++++++++--------
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp     | 12 ++++++++----
 llvm/lib/Target/AMDGPU/SIISelLowering.h       |  3 +--
 4 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index a57fd2ede44ea..82c47cce0f522 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -3527,13 +3527,6 @@ class LLVM_ABI TargetLoweringBase {
     return false;
   }
 
-  /// Return true if it is profitable to narrow a vector binop feeding an
-  /// extract_subvector into a binop of the extracted vector type.
-  virtual bool shouldNarrowExtractedVectorBinOp(EVT SrcVT, EVT SubVT,
-                                                unsigned Index) const {
-    return true;
-  }
-
   /// Return true if extraction of a scalar element from the given vector type
   /// at the given index is cheap. For example, if scalar operations occur on
   /// the same register file as vector operations, then an extract element may
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index c4c12fbf5dde6..904ae9eca08d6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27182,8 +27182,9 @@ static SDValue getSubVectorSrc(SDValue V, unsigned Index, EVT SubVT) {
   return SDValue();
 }
 
-static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
-                                              unsigned Index, const SDLoc &DL,
+static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
+                                              SDValue BinOp, unsigned Index,
+                                              const SDLoc &DL,
                                               SelectionDAG &DAG,
                                               bool LegalOperations) {
   const TargetLowering &TLI = DAG.getTargetLoweringInfo();
@@ -27192,7 +27193,7 @@ static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
     return SDValue();
 
   EVT VecVT = BinOp.getValueType();
-  if (!TLI.shouldNarrowExtractedVectorBinOp(VecVT, SubVT, Index))
+  if (!TLI.isNarrowingProfitable(N, VecVT, SubVT))
     return SDValue();
   SDValue Bop0 = BinOp.getOperand(0), Bop1 = BinOp.getOperand(1);
   if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType())
@@ -27217,13 +27218,14 @@ static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
 
 /// If we are extracting a subvector produced by a wide binary operator try
 /// to use a narrow binary operator and/or avoid concatenation and extraction.
-static SDValue narrowExtractedVectorBinOp(EVT VT, SDValue Src, unsigned Index,
-                                          const SDLoc &DL, SelectionDAG &DAG,
+static SDValue narrowExtractedVectorBinOp(SDNode *N, EVT VT, SDValue Src,
+                                          unsigned Index, const SDLoc &DL,
+                                          SelectionDAG &DAG,
                                           bool LegalOperations) {
   // TODO: Refactor with the caller (visitEXTRACT_SUBVECTOR), so we can share
   // some of these bailouts with other transforms.
 
-  if (SDValue V = narrowInsertExtractVectorBinOp(VT, Src, Index, DL, DAG,
+  if (SDValue V = narrowInsertExtractVectorBinOp(N, VT, Src, Index, DL, DAG,
                                                  LegalOperations))
     return V;
 
@@ -27689,8 +27691,8 @@ SDValue DAGCombiner::visitEXTRACT_SUBVECTOR(SDNode *N) {
           NVT, V, ExtIdx, DL, DAG, LegalOperations))
     return Shuffle;
 
-  if (SDValue NarrowBOp =
-          narrowExtractedVectorBinOp(NVT, V, ExtIdx, DL, DAG, LegalOperations))
+  if (SDValue NarrowBOp = narrowExtractedVectorBinOp(N, NVT, V, ExtIdx, DL, DAG,
+                                                     LegalOperations))
     return NarrowBOp;
 
   V = peekThroughBitcasts(V);
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index b735d00be58e7..1de00c71e74e1 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -2354,11 +2354,15 @@ bool SITargetLowering::shouldConvertConstantLoadToIntImm(const APInt &Imm,
   return true;
 }
 
-bool SITargetLowering::shouldNarrowExtractedVectorBinOp(EVT SrcVT, EVT SubVT,
-                                                        unsigned Index) const {
-  unsigned ScalarBits = SubVT.getScalarSizeInBits();
+bool SITargetLowering::isNarrowingProfitable(SDNode *N, EVT SrcVT,
+                                             EVT DestVT) const {
+  if (N->getOpcode() != ISD::EXTRACT_SUBVECTOR)
+    return AMDGPUTargetLowering::isNarrowingProfitable(N, SrcVT, DestVT);
+                                                
+  unsigned Index = N->getConstantOperandVal(1);
+  unsigned ScalarBits = DestVT.getScalarSizeInBits();
   unsigned OffsetBits = Index * ScalarBits;
-  unsigned SubvectorBits = SubVT.getSizeInBits();
+  unsigned SubvectorBits = DestVT.getSizeInBits();
 
   // If the extracted subvector starts on a dword boundary and has a dword
   // sized payload, keeping the original wide result is preferable because the
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.h b/llvm/lib/Target/AMDGPU/SIISelLowering.h
index 154faefb37ae1..eaac8072206b6 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.h
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.h
@@ -399,8 +399,7 @@ class SITargetLowering final : public AMDGPUTargetLowering {
 
   bool shouldConvertConstantLoadToIntImm(const APInt &Imm,
                                         Type *Ty) const override;
-  bool shouldNarrowExtractedVectorBinOp(EVT SrcVT, EVT SubVT,
-                                        unsigned Index) const override;
+  bool isNarrowingProfitable(SDNode *N, EVT SrcVT, EVT DestVT) const override;
   bool isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
                                unsigned Index) const override;
   bool isExtractVecEltCheap(EVT VT, unsigned Index) const override;

>From 025abc5abafed7535aafe491bb71249cb00d93b9 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Wed, 3 Jun 2026 11:10:25 +0800
Subject: [PATCH 03/15] fix format

---
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 1de00c71e74e1..805ed69174cd9 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -2358,7 +2358,7 @@ bool SITargetLowering::isNarrowingProfitable(SDNode *N, EVT SrcVT,
                                              EVT DestVT) const {
   if (N->getOpcode() != ISD::EXTRACT_SUBVECTOR)
     return AMDGPUTargetLowering::isNarrowingProfitable(N, SrcVT, DestVT);
-                                                
+
   unsigned Index = N->getConstantOperandVal(1);
   unsigned ScalarBits = DestVT.getScalarSizeInBits();
   unsigned OffsetBits = Index * ScalarBits;

>From fb51911755f79cf68f2c92885cde4ab3facfbec1 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Thu, 4 Jun 2026 14:59:11 +0800
Subject: [PATCH 04/15] blocking narrow if other non extract_subvector

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 25 ++---
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp     | 17 ----
 llvm/lib/Target/AMDGPU/SIISelLowering.h       |  2 +-
 .../extract-subvector-binop-wide-use.ll       | 29 ++++++
 llvm/test/CodeGen/X86/combine-fmul.ll         |  4 +-
 llvm/test/CodeGen/X86/fast-isel-store.ll      | 50 ++++------
 .../CodeGen/X86/machine-combiner-int-vec.ll   | 12 +--
 llvm/test/CodeGen/X86/vselect-minmax.ll       | 92 ++++++++++---------
 8 files changed, 119 insertions(+), 112 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 904ae9eca08d6..847a94ddc4236 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27182,9 +27182,8 @@ static SDValue getSubVectorSrc(SDValue V, unsigned Index, EVT SubVT) {
   return SDValue();
 }
 
-static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
-                                              SDValue BinOp, unsigned Index,
-                                              const SDLoc &DL,
+static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
+                                              unsigned Index, const SDLoc &DL,
                                               SelectionDAG &DAG,
                                               bool LegalOperations) {
   const TargetLowering &TLI = DAG.getTargetLoweringInfo();
@@ -27193,8 +27192,6 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
     return SDValue();
 
   EVT VecVT = BinOp.getValueType();
-  if (!TLI.isNarrowingProfitable(N, VecVT, SubVT))
-    return SDValue();
   SDValue Bop0 = BinOp.getOperand(0), Bop1 = BinOp.getOperand(1);
   if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType())
     return SDValue();
@@ -27210,6 +27207,13 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
   if (!Sub0 || !Sub1)
     return SDValue();
 
+  // Every user of the wide binop must be an EXTRACT_SUBVECTOR; otherwise the
+  // wide binop will still be needed and this transform would not eliminate it.
+  for (SDNode *User : BinOp->users()) {
+    if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR)
+      return SDValue();
+  }
+
   // We are inserting both operands of the wide binop only to extract back
   // to the narrow vector size. Eliminate all of the insert/extract:
   // ext (binop (ins ?, X, Index), (ins ?, Y, Index)), Index --> binop X, Y
@@ -27218,14 +27222,13 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
 
 /// If we are extracting a subvector produced by a wide binary operator try
 /// to use a narrow binary operator and/or avoid concatenation and extraction.
-static SDValue narrowExtractedVectorBinOp(SDNode *N, EVT VT, SDValue Src,
-                                          unsigned Index, const SDLoc &DL,
-                                          SelectionDAG &DAG,
+static SDValue narrowExtractedVectorBinOp(EVT VT, SDValue Src, unsigned Index,
+                                          const SDLoc &DL, SelectionDAG &DAG,
                                           bool LegalOperations) {
   // TODO: Refactor with the caller (visitEXTRACT_SUBVECTOR), so we can share
   // some of these bailouts with other transforms.
 
-  if (SDValue V = narrowInsertExtractVectorBinOp(N, VT, Src, Index, DL, DAG,
+  if (SDValue V = narrowInsertExtractVectorBinOp(VT, Src, Index, DL, DAG,
                                                  LegalOperations))
     return V;
 
@@ -27691,8 +27694,8 @@ SDValue DAGCombiner::visitEXTRACT_SUBVECTOR(SDNode *N) {
           NVT, V, ExtIdx, DL, DAG, LegalOperations))
     return Shuffle;
 
-  if (SDValue NarrowBOp = narrowExtractedVectorBinOp(N, NVT, V, ExtIdx, DL, DAG,
-                                                     LegalOperations))
+  if (SDValue NarrowBOp =
+          narrowExtractedVectorBinOp(NVT, V, ExtIdx, DL, DAG, LegalOperations))
     return NarrowBOp;
 
   V = peekThroughBitcasts(V);
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 805ed69174cd9..f49b1efb9808d 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -2354,23 +2354,6 @@ bool SITargetLowering::shouldConvertConstantLoadToIntImm(const APInt &Imm,
   return true;
 }
 
-bool SITargetLowering::isNarrowingProfitable(SDNode *N, EVT SrcVT,
-                                             EVT DestVT) const {
-  if (N->getOpcode() != ISD::EXTRACT_SUBVECTOR)
-    return AMDGPUTargetLowering::isNarrowingProfitable(N, SrcVT, DestVT);
-
-  unsigned Index = N->getConstantOperandVal(1);
-  unsigned ScalarBits = DestVT.getScalarSizeInBits();
-  unsigned OffsetBits = Index * ScalarBits;
-  unsigned SubvectorBits = DestVT.getSizeInBits();
-
-  // If the extracted subvector starts on a dword boundary and has a dword
-  // sized payload, keeping the original wide result is preferable because the
-  // subvector can be referenced directly through subregisters. Otherwise,
-  // narrowing may avoid element extraction or reconstruction.
-  return OffsetBits % 32 != 0 || SubvectorBits % 32 != 0;
-}
-
 bool SITargetLowering::isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
                                                unsigned Index) const {
   if (!isOperationLegalOrCustom(ISD::EXTRACT_SUBVECTOR, ResVT))
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.h b/llvm/lib/Target/AMDGPU/SIISelLowering.h
index eaac8072206b6..95ff5bba7cfff 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.h
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.h
@@ -399,7 +399,7 @@ class SITargetLowering final : public AMDGPUTargetLowering {
 
   bool shouldConvertConstantLoadToIntImm(const APInt &Imm,
                                         Type *Ty) const override;
-  bool isNarrowingProfitable(SDNode *N, EVT SrcVT, EVT DestVT) const override;
+
   bool isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
                                unsigned Index) const override;
   bool isExtractVecEltCheap(EVT VT, unsigned Index) const override;
diff --git a/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll b/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
new file mode 100644
index 0000000000000..c4dcf7c4405b3
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
@@ -0,0 +1,29 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=amdgcn -mcpu=gfx1250 < %s | FileCheck --check-prefix=GFX1250 %s
+
+define <4 x i32> @insert_extract_and_inreg_v4i32(ptr addrspace(1) inreg %out_sub, <2 x i32> inreg %a, <2 x i32> inreg %b) {
+; GFX1250-LABEL: insert_extract_and_inreg_v4i32:
+; GFX1250:       ; %bb.0: ; %entry
+; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX1250-NEXT:    s_wait_kmcnt 0x0
+; GFX1250-NEXT:    s_and_b32 s2, s2, s16
+; GFX1250-NEXT:    s_and_b32 s3, s3, s17
+; GFX1250-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX1250-NEXT:    v_dual_mov_b32 v0, s2 :: v_dual_mov_b32 v1, s3
+; GFX1250-NEXT:    v_dual_mov_b32 v2, 0 :: v_dual_mov_b32 v3, s3
+; GFX1250-NEXT:    global_store_b64 v2, v[0:1], s[0:1]
+; GFX1250-NEXT:    s_wait_xcnt 0x0
+; GFX1250-NEXT:    v_dual_mov_b32 v0, 0 :: v_dual_mov_b32 v1, 0
+; GFX1250-NEXT:    v_mov_b32_e32 v2, s2
+; GFX1250-NEXT:    s_set_pc_i64 s[30:31]
+entry:
+  %v0 = call <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32> zeroinitializer, <2 x i32> %a, i64 2)
+  %v1 = call <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32> zeroinitializer, <2 x i32> %b, i64 2)
+  %r = and <4 x i32> %v0, %v1
+  %sub = call <2 x i32> @llvm.vector.extract.v2i32.v4i32(<4 x i32> %r, i64 2)
+  store <2 x i32> %sub, ptr addrspace(1) %out_sub, align 8
+  ret <4 x i32> %r
+}
+
+declare <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32>, <2 x i32>, i64 immarg)
+declare <2 x i32> @llvm.vector.extract.v2i32.v4i32(<4 x i32>, i64 immarg)
diff --git a/llvm/test/CodeGen/X86/combine-fmul.ll b/llvm/test/CodeGen/X86/combine-fmul.ll
index c5966a81147e9..6f8a9b9350755 100644
--- a/llvm/test/CodeGen/X86/combine-fmul.ll
+++ b/llvm/test/CodeGen/X86/combine-fmul.ll
@@ -121,10 +121,10 @@ define <16 x float> @concat_fmul_self_v16f32_v4f32(<4 x float> %a0, <4 x float>
 define <8 x double> @concat_fmul_self_v8f64_v4f64(<4 x double> %a0, <4 x double> %a1) {
 ; SSE-LABEL: concat_fmul_self_v8f64_v4f64:
 ; SSE:       # %bb.0:
-; SSE-NEXT:    mulpd %xmm0, %xmm0
 ; SSE-NEXT:    mulpd %xmm1, %xmm1
 ; SSE-NEXT:    mulpd %xmm2, %xmm2
 ; SSE-NEXT:    mulpd %xmm3, %xmm3
+; SSE-NEXT:    mulpd %xmm0, %xmm0
 ; SSE-NEXT:    retq
 ;
 ; AVX1OR2-LABEL: concat_fmul_self_v8f64_v4f64:
@@ -148,10 +148,10 @@ define <8 x double> @concat_fmul_self_v8f64_v4f64(<4 x double> %a0, <4 x double>
 define <16 x float> @concat_fmul_self_v16f32_v8f32(<8 x float> %a0, <8 x float> %a1) {
 ; SSE-LABEL: concat_fmul_self_v16f32_v8f32:
 ; SSE:       # %bb.0:
-; SSE-NEXT:    mulps %xmm0, %xmm0
 ; SSE-NEXT:    mulps %xmm1, %xmm1
 ; SSE-NEXT:    mulps %xmm2, %xmm2
 ; SSE-NEXT:    mulps %xmm3, %xmm3
+; SSE-NEXT:    mulps %xmm0, %xmm0
 ; SSE-NEXT:    retq
 ;
 ; AVX1OR2-LABEL: concat_fmul_self_v16f32_v8f32:
diff --git a/llvm/test/CodeGen/X86/fast-isel-store.ll b/llvm/test/CodeGen/X86/fast-isel-store.ll
index eba538d213392..b804239b3f418 100644
--- a/llvm/test/CodeGen/X86/fast-isel-store.ll
+++ b/llvm/test/CodeGen/X86/fast-isel-store.ll
@@ -337,20 +337,19 @@ define <4 x double> @test_store_4xf64(ptr nocapture %addr, <4 x double> %value,
 ; X86-SSE-LABEL: test_store_4xf64:
 ; X86-SSE:       # %bb.0:
 ; X86-SSE-NEXT:    subl $12, %esp
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm3
 ; X86-SSE-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm1
 ; X86-SSE-NEXT:    addpd %xmm2, %xmm0
 ; X86-SSE-NEXT:    movupd %xmm0, (%eax)
-; X86-SSE-NEXT:    addpd %xmm3, %xmm1
 ; X86-SSE-NEXT:    movupd %xmm1, 16(%eax)
 ; X86-SSE-NEXT:    addl $12, %esp
 ; X86-SSE-NEXT:    retl
 ;
 ; X64-SSE-LABEL: test_store_4xf64:
 ; X64-SSE:       # %bb.0:
+; X64-SSE-NEXT:    addpd %xmm3, %xmm1
 ; X64-SSE-NEXT:    addpd %xmm2, %xmm0
 ; X64-SSE-NEXT:    movupd %xmm0, (%rdi)
-; X64-SSE-NEXT:    addpd %xmm3, %xmm1
 ; X64-SSE-NEXT:    movupd %xmm1, 16(%rdi)
 ; X64-SSE-NEXT:    retq
 ;
@@ -375,20 +374,19 @@ define <4 x double> @test_store_4xf64_aligned(ptr nocapture %addr, <4 x double>
 ; X86-SSE-LABEL: test_store_4xf64_aligned:
 ; X86-SSE:       # %bb.0:
 ; X86-SSE-NEXT:    subl $12, %esp
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm3
 ; X86-SSE-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm1
 ; X86-SSE-NEXT:    addpd %xmm2, %xmm0
 ; X86-SSE-NEXT:    movapd %xmm0, (%eax)
-; X86-SSE-NEXT:    addpd %xmm3, %xmm1
 ; X86-SSE-NEXT:    movapd %xmm1, 16(%eax)
 ; X86-SSE-NEXT:    addl $12, %esp
 ; X86-SSE-NEXT:    retl
 ;
 ; X64-SSE-LABEL: test_store_4xf64_aligned:
 ; X64-SSE:       # %bb.0:
+; X64-SSE-NEXT:    addpd %xmm3, %xmm1
 ; X64-SSE-NEXT:    addpd %xmm2, %xmm0
 ; X64-SSE-NEXT:    movapd %xmm0, (%rdi)
-; X64-SSE-NEXT:    addpd %xmm3, %xmm1
 ; X64-SSE-NEXT:    movapd %xmm1, 16(%rdi)
 ; X64-SSE-NEXT:    retq
 ;
@@ -605,17 +603,14 @@ define <8 x double> @test_store_8xf64(ptr nocapture %addr, <8 x double> %value,
 ; X86-SSE-LABEL: test_store_8xf64:
 ; X86-SSE:       # %bb.0:
 ; X86-SSE-NEXT:    subl $12, %esp
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm4
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm5
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm6
 ; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm3
-; X86-SSE-NEXT:    addpd %xmm4, %xmm3
 ; X86-SSE-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm3
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm2
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm1
 ; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm0
 ; X86-SSE-NEXT:    movupd %xmm0, (%eax)
-; X86-SSE-NEXT:    addpd %xmm6, %xmm1
 ; X86-SSE-NEXT:    movupd %xmm1, 16(%eax)
-; X86-SSE-NEXT:    addpd %xmm5, %xmm2
 ; X86-SSE-NEXT:    movupd %xmm2, 32(%eax)
 ; X86-SSE-NEXT:    movupd %xmm3, 48(%eax)
 ; X86-SSE-NEXT:    addl $12, %esp
@@ -623,13 +618,13 @@ define <8 x double> @test_store_8xf64(ptr nocapture %addr, <8 x double> %value,
 ;
 ; X64-SSE-LABEL: test_store_8xf64:
 ; X64-SSE:       # %bb.0:
+; X64-SSE-NEXT:    addpd %xmm7, %xmm3
+; X64-SSE-NEXT:    addpd %xmm6, %xmm2
+; X64-SSE-NEXT:    addpd %xmm5, %xmm1
 ; X64-SSE-NEXT:    addpd %xmm4, %xmm0
 ; X64-SSE-NEXT:    movupd %xmm0, (%rdi)
-; X64-SSE-NEXT:    addpd %xmm5, %xmm1
 ; X64-SSE-NEXT:    movupd %xmm1, 16(%rdi)
-; X64-SSE-NEXT:    addpd %xmm6, %xmm2
 ; X64-SSE-NEXT:    movupd %xmm2, 32(%rdi)
-; X64-SSE-NEXT:    addpd %xmm7, %xmm3
 ; X64-SSE-NEXT:    movupd %xmm3, 48(%rdi)
 ; X64-SSE-NEXT:    retq
 ;
@@ -639,11 +634,10 @@ define <8 x double> @test_store_8xf64(ptr nocapture %addr, <8 x double> %value,
 ; X86-AVX1-NEXT:    movl %esp, %ebp
 ; X86-AVX1-NEXT:    andl $-32, %esp
 ; X86-AVX1-NEXT:    subl $32, %esp
-; X86-AVX1-NEXT:    vmovapd 40(%ebp), %ymm3
 ; X86-AVX1-NEXT:    movl 8(%ebp), %eax
+; X86-AVX1-NEXT:    vaddpd 40(%ebp), %ymm1, %ymm1
 ; X86-AVX1-NEXT:    vaddpd %ymm2, %ymm0, %ymm0
 ; X86-AVX1-NEXT:    vmovupd %ymm0, (%eax)
-; X86-AVX1-NEXT:    vaddpd %ymm3, %ymm1, %ymm1
 ; X86-AVX1-NEXT:    vmovupd %ymm1, 32(%eax)
 ; X86-AVX1-NEXT:    movl %ebp, %esp
 ; X86-AVX1-NEXT:    popl %ebp
@@ -651,9 +645,9 @@ define <8 x double> @test_store_8xf64(ptr nocapture %addr, <8 x double> %value,
 ;
 ; X64-AVX1-LABEL: test_store_8xf64:
 ; X64-AVX1:       # %bb.0:
+; X64-AVX1-NEXT:    vaddpd %ymm3, %ymm1, %ymm1
 ; X64-AVX1-NEXT:    vaddpd %ymm2, %ymm0, %ymm0
 ; X64-AVX1-NEXT:    vmovupd %ymm0, (%rdi)
-; X64-AVX1-NEXT:    vaddpd %ymm3, %ymm1, %ymm1
 ; X64-AVX1-NEXT:    vmovupd %ymm1, 32(%rdi)
 ; X64-AVX1-NEXT:    retq
 ;
@@ -678,17 +672,14 @@ define <8 x double> @test_store_8xf64_aligned(ptr nocapture %addr, <8 x double>
 ; X86-SSE-LABEL: test_store_8xf64_aligned:
 ; X86-SSE:       # %bb.0:
 ; X86-SSE-NEXT:    subl $12, %esp
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm4
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm5
-; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm6
 ; X86-SSE-NEXT:    movapd {{[0-9]+}}(%esp), %xmm3
-; X86-SSE-NEXT:    addpd %xmm4, %xmm3
 ; X86-SSE-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm3
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm2
+; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm1
 ; X86-SSE-NEXT:    addpd {{[0-9]+}}(%esp), %xmm0
 ; X86-SSE-NEXT:    movapd %xmm0, (%eax)
-; X86-SSE-NEXT:    addpd %xmm6, %xmm1
 ; X86-SSE-NEXT:    movapd %xmm1, 16(%eax)
-; X86-SSE-NEXT:    addpd %xmm5, %xmm2
 ; X86-SSE-NEXT:    movapd %xmm2, 32(%eax)
 ; X86-SSE-NEXT:    movapd %xmm3, 48(%eax)
 ; X86-SSE-NEXT:    addl $12, %esp
@@ -696,13 +687,13 @@ define <8 x double> @test_store_8xf64_aligned(ptr nocapture %addr, <8 x double>
 ;
 ; X64-SSE-LABEL: test_store_8xf64_aligned:
 ; X64-SSE:       # %bb.0:
+; X64-SSE-NEXT:    addpd %xmm7, %xmm3
+; X64-SSE-NEXT:    addpd %xmm6, %xmm2
+; X64-SSE-NEXT:    addpd %xmm5, %xmm1
 ; X64-SSE-NEXT:    addpd %xmm4, %xmm0
 ; X64-SSE-NEXT:    movapd %xmm0, (%rdi)
-; X64-SSE-NEXT:    addpd %xmm5, %xmm1
 ; X64-SSE-NEXT:    movapd %xmm1, 16(%rdi)
-; X64-SSE-NEXT:    addpd %xmm6, %xmm2
 ; X64-SSE-NEXT:    movapd %xmm2, 32(%rdi)
-; X64-SSE-NEXT:    addpd %xmm7, %xmm3
 ; X64-SSE-NEXT:    movapd %xmm3, 48(%rdi)
 ; X64-SSE-NEXT:    retq
 ;
@@ -712,11 +703,10 @@ define <8 x double> @test_store_8xf64_aligned(ptr nocapture %addr, <8 x double>
 ; X86-AVX1-NEXT:    movl %esp, %ebp
 ; X86-AVX1-NEXT:    andl $-32, %esp
 ; X86-AVX1-NEXT:    subl $32, %esp
-; X86-AVX1-NEXT:    vmovapd 40(%ebp), %ymm3
 ; X86-AVX1-NEXT:    movl 8(%ebp), %eax
+; X86-AVX1-NEXT:    vaddpd 40(%ebp), %ymm1, %ymm1
 ; X86-AVX1-NEXT:    vaddpd %ymm2, %ymm0, %ymm0
 ; X86-AVX1-NEXT:    vmovapd %ymm0, (%eax)
-; X86-AVX1-NEXT:    vaddpd %ymm3, %ymm1, %ymm1
 ; X86-AVX1-NEXT:    vmovapd %ymm1, 32(%eax)
 ; X86-AVX1-NEXT:    movl %ebp, %esp
 ; X86-AVX1-NEXT:    popl %ebp
@@ -724,9 +714,9 @@ define <8 x double> @test_store_8xf64_aligned(ptr nocapture %addr, <8 x double>
 ;
 ; X64-AVX1-LABEL: test_store_8xf64_aligned:
 ; X64-AVX1:       # %bb.0:
+; X64-AVX1-NEXT:    vaddpd %ymm3, %ymm1, %ymm1
 ; X64-AVX1-NEXT:    vaddpd %ymm2, %ymm0, %ymm0
 ; X64-AVX1-NEXT:    vmovapd %ymm0, (%rdi)
-; X64-AVX1-NEXT:    vaddpd %ymm3, %ymm1, %ymm1
 ; X64-AVX1-NEXT:    vmovapd %ymm1, 32(%rdi)
 ; X64-AVX1-NEXT:    retq
 ;
diff --git a/llvm/test/CodeGen/X86/machine-combiner-int-vec.ll b/llvm/test/CodeGen/X86/machine-combiner-int-vec.ll
index ee42cb8126f74..9cb026ff009c7 100644
--- a/llvm/test/CodeGen/X86/machine-combiner-int-vec.ll
+++ b/llvm/test/CodeGen/X86/machine-combiner-int-vec.ll
@@ -91,10 +91,10 @@ define <4 x i32> @reassociate_xor_v4i32(<4 x i32> %x0, <4 x i32> %x1, <4 x i32>
 define <8 x i32> @reassociate_and_v8i32(<8 x i32> %x0, <8 x i32> %x1, <8 x i32> %x2, <8 x i32> %x3) {
 ; SSE-LABEL: reassociate_and_v8i32:
 ; SSE:       # %bb.0:
+; SSE-NEXT:    paddd %xmm3, %xmm1
 ; SSE-NEXT:    paddd %xmm2, %xmm0
 ; SSE-NEXT:    pand %xmm6, %xmm4
 ; SSE-NEXT:    pand %xmm4, %xmm0
-; SSE-NEXT:    paddd %xmm3, %xmm1
 ; SSE-NEXT:    pand %xmm7, %xmm5
 ; SSE-NEXT:    pand %xmm5, %xmm1
 ; SSE-NEXT:    retq
@@ -121,10 +121,10 @@ define <8 x i32> @reassociate_and_v8i32(<8 x i32> %x0, <8 x i32> %x1, <8 x i32>
 define <8 x i32> @reassociate_or_v8i32(<8 x i32> %x0, <8 x i32> %x1, <8 x i32> %x2, <8 x i32> %x3) {
 ; SSE-LABEL: reassociate_or_v8i32:
 ; SSE:       # %bb.0:
+; SSE-NEXT:    paddd %xmm3, %xmm1
 ; SSE-NEXT:    paddd %xmm2, %xmm0
 ; SSE-NEXT:    por %xmm6, %xmm4
 ; SSE-NEXT:    por %xmm4, %xmm0
-; SSE-NEXT:    paddd %xmm3, %xmm1
 ; SSE-NEXT:    por %xmm7, %xmm5
 ; SSE-NEXT:    por %xmm5, %xmm1
 ; SSE-NEXT:    retq
@@ -151,10 +151,10 @@ define <8 x i32> @reassociate_or_v8i32(<8 x i32> %x0, <8 x i32> %x1, <8 x i32> %
 define <8 x i32> @reassociate_xor_v8i32(<8 x i32> %x0, <8 x i32> %x1, <8 x i32> %x2, <8 x i32> %x3) {
 ; SSE-LABEL: reassociate_xor_v8i32:
 ; SSE:       # %bb.0:
+; SSE-NEXT:    paddd %xmm3, %xmm1
 ; SSE-NEXT:    paddd %xmm2, %xmm0
 ; SSE-NEXT:    pxor %xmm6, %xmm4
 ; SSE-NEXT:    pxor %xmm4, %xmm0
-; SSE-NEXT:    paddd %xmm3, %xmm1
 ; SSE-NEXT:    pxor %xmm7, %xmm5
 ; SSE-NEXT:    pxor %xmm5, %xmm1
 ; SSE-NEXT:    retq
@@ -200,10 +200,10 @@ define <16 x i32> @reassociate_and_v16i32(<16 x i32> %x0, <16 x i32> %x1, <16 x
 ;
 ; AVX2-LABEL: reassociate_and_v16i32:
 ; AVX2:       # %bb.0:
+; AVX2-NEXT:    vpaddd %ymm3, %ymm1, %ymm1
 ; AVX2-NEXT:    vpaddd %ymm2, %ymm0, %ymm0
 ; AVX2-NEXT:    vpand %ymm4, %ymm6, %ymm2
 ; AVX2-NEXT:    vpand %ymm0, %ymm2, %ymm0
-; AVX2-NEXT:    vpaddd %ymm3, %ymm1, %ymm1
 ; AVX2-NEXT:    vpand %ymm5, %ymm7, %ymm2
 ; AVX2-NEXT:    vpand %ymm1, %ymm2, %ymm1
 ; AVX2-NEXT:    retq
@@ -239,10 +239,10 @@ define <16 x i32> @reassociate_or_v16i32(<16 x i32> %x0, <16 x i32> %x1, <16 x i
 ;
 ; AVX2-LABEL: reassociate_or_v16i32:
 ; AVX2:       # %bb.0:
+; AVX2-NEXT:    vpaddd %ymm3, %ymm1, %ymm1
 ; AVX2-NEXT:    vpaddd %ymm2, %ymm0, %ymm0
 ; AVX2-NEXT:    vpor %ymm4, %ymm6, %ymm2
 ; AVX2-NEXT:    vpor %ymm0, %ymm2, %ymm0
-; AVX2-NEXT:    vpaddd %ymm3, %ymm1, %ymm1
 ; AVX2-NEXT:    vpor %ymm5, %ymm7, %ymm2
 ; AVX2-NEXT:    vpor %ymm1, %ymm2, %ymm1
 ; AVX2-NEXT:    retq
@@ -278,10 +278,10 @@ define <16 x i32> @reassociate_xor_v16i32(<16 x i32> %x0, <16 x i32> %x1, <16 x
 ;
 ; AVX2-LABEL: reassociate_xor_v16i32:
 ; AVX2:       # %bb.0:
+; AVX2-NEXT:    vpaddd %ymm3, %ymm1, %ymm1
 ; AVX2-NEXT:    vpaddd %ymm2, %ymm0, %ymm0
 ; AVX2-NEXT:    vpxor %ymm4, %ymm6, %ymm2
 ; AVX2-NEXT:    vpxor %ymm0, %ymm2, %ymm0
-; AVX2-NEXT:    vpaddd %ymm3, %ymm1, %ymm1
 ; AVX2-NEXT:    vpxor %ymm5, %ymm7, %ymm2
 ; AVX2-NEXT:    vpxor %ymm1, %ymm2, %ymm1
 ; AVX2-NEXT:    retq
diff --git a/llvm/test/CodeGen/X86/vselect-minmax.ll b/llvm/test/CodeGen/X86/vselect-minmax.ll
index cb0542ca7cea8..2352b6d6c0b8f 100644
--- a/llvm/test/CodeGen/X86/vselect-minmax.ll
+++ b/llvm/test/CodeGen/X86/vselect-minmax.ll
@@ -10284,69 +10284,71 @@ define <8 x i64> @concat_smin_smax(<4 x i64> %a0, <4 x i64> %a1) {
 ; SSE2-LABEL: concat_smin_smax:
 ; SSE2:       # %bb.0:
 ; SSE2-NEXT:    movdqa {{.*#+}} xmm6 = [2147483648,2147483648]
-; SSE2-NEXT:    movdqa %xmm0, %xmm4
+; SSE2-NEXT:    movdqa %xmm3, %xmm4
 ; SSE2-NEXT:    pxor %xmm6, %xmm4
-; SSE2-NEXT:    movdqa %xmm2, %xmm5
+; SSE2-NEXT:    movdqa %xmm1, %xmm5
 ; SSE2-NEXT:    pxor %xmm6, %xmm5
 ; SSE2-NEXT:    movdqa %xmm5, %xmm7
 ; SSE2-NEXT:    pcmpgtd %xmm4, %xmm7
 ; SSE2-NEXT:    pshufd {{.*#+}} xmm8 = xmm7[0,0,2,2]
 ; SSE2-NEXT:    pcmpeqd %xmm4, %xmm5
-; SSE2-NEXT:    pshufd {{.*#+}} xmm4 = xmm5[1,1,3,3]
-; SSE2-NEXT:    pand %xmm8, %xmm4
-; SSE2-NEXT:    pshufd {{.*#+}} xmm5 = xmm7[1,1,3,3]
-; SSE2-NEXT:    por %xmm4, %xmm5
-; SSE2-NEXT:    movdqa %xmm5, %xmm7
-; SSE2-NEXT:    pandn %xmm2, %xmm7
-; SSE2-NEXT:    movdqa %xmm0, %xmm4
-; SSE2-NEXT:    pand %xmm5, %xmm4
-; SSE2-NEXT:    por %xmm7, %xmm4
-; SSE2-NEXT:    movdqa %xmm1, %xmm7
+; SSE2-NEXT:    pshufd {{.*#+}} xmm5 = xmm5[1,1,3,3]
+; SSE2-NEXT:    pand %xmm8, %xmm5
+; SSE2-NEXT:    pshufd {{.*#+}} xmm4 = xmm7[1,1,3,3]
+; SSE2-NEXT:    por %xmm5, %xmm4
+; SSE2-NEXT:    movdqa %xmm4, %xmm7
+; SSE2-NEXT:    pandn %xmm1, %xmm7
+; SSE2-NEXT:    movdqa %xmm3, %xmm5
+; SSE2-NEXT:    pand %xmm4, %xmm5
+; SSE2-NEXT:    por %xmm7, %xmm5
+; SSE2-NEXT:    movdqa %xmm2, %xmm7
 ; SSE2-NEXT:    pxor %xmm6, %xmm7
-; SSE2-NEXT:    pxor %xmm3, %xmm6
+; SSE2-NEXT:    pxor %xmm0, %xmm6
 ; SSE2-NEXT:    movdqa %xmm6, %xmm8
 ; SSE2-NEXT:    pcmpgtd %xmm7, %xmm8
 ; SSE2-NEXT:    pshufd {{.*#+}} xmm9 = xmm8[0,0,2,2]
 ; SSE2-NEXT:    pcmpeqd %xmm7, %xmm6
-; SSE2-NEXT:    pshufd {{.*#+}} xmm6 = xmm6[1,1,3,3]
-; SSE2-NEXT:    pand %xmm9, %xmm6
-; SSE2-NEXT:    pshufd {{.*#+}} xmm7 = xmm8[1,1,3,3]
-; SSE2-NEXT:    por %xmm6, %xmm7
-; SSE2-NEXT:    movdqa %xmm7, %xmm6
-; SSE2-NEXT:    pandn %xmm3, %xmm6
-; SSE2-NEXT:    movdqa %xmm7, %xmm8
-; SSE2-NEXT:    pandn %xmm1, %xmm8
-; SSE2-NEXT:    pand %xmm7, %xmm1
-; SSE2-NEXT:    por %xmm6, %xmm1
-; SSE2-NEXT:    pand %xmm5, %xmm2
-; SSE2-NEXT:    pandn %xmm0, %xmm5
-; SSE2-NEXT:    por %xmm5, %xmm2
-; SSE2-NEXT:    pand %xmm7, %xmm3
-; SSE2-NEXT:    por %xmm8, %xmm3
-; SSE2-NEXT:    movdqa %xmm4, %xmm0
+; SSE2-NEXT:    pshufd {{.*#+}} xmm7 = xmm6[1,1,3,3]
+; SSE2-NEXT:    pand %xmm9, %xmm7
+; SSE2-NEXT:    pshufd {{.*#+}} xmm6 = xmm8[1,1,3,3]
+; SSE2-NEXT:    por %xmm7, %xmm6
+; SSE2-NEXT:    movdqa %xmm6, %xmm7
+; SSE2-NEXT:    pandn %xmm2, %xmm7
+; SSE2-NEXT:    movdqa %xmm6, %xmm8
+; SSE2-NEXT:    pandn %xmm0, %xmm8
+; SSE2-NEXT:    pand %xmm6, %xmm0
+; SSE2-NEXT:    por %xmm0, %xmm7
+; SSE2-NEXT:    pand %xmm4, %xmm1
+; SSE2-NEXT:    pandn %xmm3, %xmm4
+; SSE2-NEXT:    por %xmm1, %xmm4
+; SSE2-NEXT:    pand %xmm2, %xmm6
+; SSE2-NEXT:    por %xmm8, %xmm6
+; SSE2-NEXT:    movdqa %xmm6, %xmm0
+; SSE2-NEXT:    movdqa %xmm5, %xmm1
+; SSE2-NEXT:    movdqa %xmm7, %xmm2
+; SSE2-NEXT:    movdqa %xmm4, %xmm3
 ; SSE2-NEXT:    retq
 ;
 ; SSE4-LABEL: concat_smin_smax:
 ; SSE4:       # %bb.0:
-; SSE4-NEXT:    movdqa %xmm1, %xmm4
-; SSE4-NEXT:    movdqa %xmm0, %xmm5
+; SSE4-NEXT:    movdqa %xmm0, %xmm4
+; SSE4-NEXT:    movdqa %xmm1, %xmm5
+; SSE4-NEXT:    pcmpgtq %xmm3, %xmm5
+; SSE4-NEXT:    movdqa %xmm1, %xmm6
+; SSE4-NEXT:    movdqa %xmm5, %xmm0
+; SSE4-NEXT:    blendvpd %xmm0, %xmm3, %xmm6
+; SSE4-NEXT:    movdqa %xmm4, %xmm7
+; SSE4-NEXT:    pcmpgtq %xmm2, %xmm7
 ; SSE4-NEXT:    movdqa %xmm2, %xmm8
-; SSE4-NEXT:    pcmpgtq %xmm0, %xmm8
-; SSE4-NEXT:    movdqa %xmm2, %xmm6
-; SSE4-NEXT:    movdqa %xmm8, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm5, %xmm6
-; SSE4-NEXT:    movdqa %xmm3, %xmm7
-; SSE4-NEXT:    pcmpgtq %xmm1, %xmm7
-; SSE4-NEXT:    movdqa %xmm3, %xmm1
 ; SSE4-NEXT:    movdqa %xmm7, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm4, %xmm1
-; SSE4-NEXT:    movdqa %xmm8, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm2, %xmm5
+; SSE4-NEXT:    blendvpd %xmm0, %xmm4, %xmm8
+; SSE4-NEXT:    movdqa %xmm5, %xmm0
+; SSE4-NEXT:    blendvpd %xmm0, %xmm1, %xmm3
 ; SSE4-NEXT:    movdqa %xmm7, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm3, %xmm4
-; SSE4-NEXT:    movapd %xmm6, %xmm0
-; SSE4-NEXT:    movapd %xmm5, %xmm2
-; SSE4-NEXT:    movapd %xmm4, %xmm3
+; SSE4-NEXT:    blendvpd %xmm0, %xmm2, %xmm4
+; SSE4-NEXT:    movapd %xmm4, %xmm0
+; SSE4-NEXT:    movapd %xmm6, %xmm1
+; SSE4-NEXT:    movapd %xmm8, %xmm2
 ; SSE4-NEXT:    retq
 ;
 ; AVX1-LABEL: concat_smin_smax:

>From 587967a547aba64ebf5b67af50eb1910e4f719b2 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Thu, 4 Jun 2026 17:30:02 +0800
Subject: [PATCH 05/15] fix bug

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 25 +++++---
 llvm/test/CodeGen/X86/combine-fmul.ll         |  4 +-
 llvm/test/CodeGen/X86/nontemporal-2.ll        |  2 +-
 llvm/test/CodeGen/X86/vselect-minmax.ll       | 61 +++++++++----------
 4 files changed, 48 insertions(+), 44 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 847a94ddc4236..b66933120ecc3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27182,8 +27182,9 @@ static SDValue getSubVectorSrc(SDValue V, unsigned Index, EVT SubVT) {
   return SDValue();
 }
 
-static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
-                                              unsigned Index, const SDLoc &DL,
+static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
+                                              SDValue BinOp, unsigned Index,
+                                              const SDLoc &DL,
                                               SelectionDAG &DAG,
                                               bool LegalOperations) {
   const TargetLowering &TLI = DAG.getTargetLoweringInfo();
@@ -27207,10 +27208,13 @@ static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
   if (!Sub0 || !Sub1)
     return SDValue();
 
-  // Every user of the wide binop must be an EXTRACT_SUBVECTOR; otherwise the
-  // wide binop will still be needed and this transform would not eliminate it.
+  // Every other user of the wide binop must be an EXTRACT_SUBVECTOR that is
+  // still on the combiner worklist; otherwise the wide binop may remain needed.
   for (SDNode *User : BinOp->users()) {
-    if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR)
+    if (User == N)
+      continue;
+    if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR ||
+        User->getCombinerWorklistIndex() < 0)
       return SDValue();
   }
 
@@ -27222,13 +27226,14 @@ static SDValue narrowInsertExtractVectorBinOp(EVT SubVT, SDValue BinOp,
 
 /// If we are extracting a subvector produced by a wide binary operator try
 /// to use a narrow binary operator and/or avoid concatenation and extraction.
-static SDValue narrowExtractedVectorBinOp(EVT VT, SDValue Src, unsigned Index,
-                                          const SDLoc &DL, SelectionDAG &DAG,
+static SDValue narrowExtractedVectorBinOp(SDNode *N, EVT VT, SDValue Src,
+                                          unsigned Index, const SDLoc &DL,
+                                          SelectionDAG &DAG,
                                           bool LegalOperations) {
   // TODO: Refactor with the caller (visitEXTRACT_SUBVECTOR), so we can share
   // some of these bailouts with other transforms.
 
-  if (SDValue V = narrowInsertExtractVectorBinOp(VT, Src, Index, DL, DAG,
+  if (SDValue V = narrowInsertExtractVectorBinOp(N, VT, Src, Index, DL, DAG,
                                                  LegalOperations))
     return V;
 
@@ -27694,8 +27699,8 @@ SDValue DAGCombiner::visitEXTRACT_SUBVECTOR(SDNode *N) {
           NVT, V, ExtIdx, DL, DAG, LegalOperations))
     return Shuffle;
 
-  if (SDValue NarrowBOp =
-          narrowExtractedVectorBinOp(NVT, V, ExtIdx, DL, DAG, LegalOperations))
+  if (SDValue NarrowBOp = narrowExtractedVectorBinOp(N, NVT, V, ExtIdx, DL, DAG,
+                                                     LegalOperations))
     return NarrowBOp;
 
   V = peekThroughBitcasts(V);
diff --git a/llvm/test/CodeGen/X86/combine-fmul.ll b/llvm/test/CodeGen/X86/combine-fmul.ll
index 6f8a9b9350755..c5966a81147e9 100644
--- a/llvm/test/CodeGen/X86/combine-fmul.ll
+++ b/llvm/test/CodeGen/X86/combine-fmul.ll
@@ -121,10 +121,10 @@ define <16 x float> @concat_fmul_self_v16f32_v4f32(<4 x float> %a0, <4 x float>
 define <8 x double> @concat_fmul_self_v8f64_v4f64(<4 x double> %a0, <4 x double> %a1) {
 ; SSE-LABEL: concat_fmul_self_v8f64_v4f64:
 ; SSE:       # %bb.0:
+; SSE-NEXT:    mulpd %xmm0, %xmm0
 ; SSE-NEXT:    mulpd %xmm1, %xmm1
 ; SSE-NEXT:    mulpd %xmm2, %xmm2
 ; SSE-NEXT:    mulpd %xmm3, %xmm3
-; SSE-NEXT:    mulpd %xmm0, %xmm0
 ; SSE-NEXT:    retq
 ;
 ; AVX1OR2-LABEL: concat_fmul_self_v8f64_v4f64:
@@ -148,10 +148,10 @@ define <8 x double> @concat_fmul_self_v8f64_v4f64(<4 x double> %a0, <4 x double>
 define <16 x float> @concat_fmul_self_v16f32_v8f32(<8 x float> %a0, <8 x float> %a1) {
 ; SSE-LABEL: concat_fmul_self_v16f32_v8f32:
 ; SSE:       # %bb.0:
+; SSE-NEXT:    mulps %xmm0, %xmm0
 ; SSE-NEXT:    mulps %xmm1, %xmm1
 ; SSE-NEXT:    mulps %xmm2, %xmm2
 ; SSE-NEXT:    mulps %xmm3, %xmm3
-; SSE-NEXT:    mulps %xmm0, %xmm0
 ; SSE-NEXT:    retq
 ;
 ; AVX1OR2-LABEL: concat_fmul_self_v16f32_v8f32:
diff --git a/llvm/test/CodeGen/X86/nontemporal-2.ll b/llvm/test/CodeGen/X86/nontemporal-2.ll
index e5f217301956a..f9795d869b3e7 100644
--- a/llvm/test/CodeGen/X86/nontemporal-2.ll
+++ b/llvm/test/CodeGen/X86/nontemporal-2.ll
@@ -1234,9 +1234,9 @@ define void @test_op_v32i8(<32 x i8> %a, <32 x i8> %b, ptr %dst) {
 define void @test_unaligned_v8f32(<8 x float> %a, <8 x float> %b, ptr %dst) {
 ; SSE-LABEL: test_unaligned_v8f32:
 ; SSE:       # %bb.0:
+; SSE-NEXT:    addps %xmm2, %xmm0
 ; SSE-NEXT:    addps %xmm3, %xmm1
 ; SSE-NEXT:    movntps %xmm1, 16(%rdi)
-; SSE-NEXT:    addps %xmm2, %xmm0
 ; SSE-NEXT:    movntps %xmm0, (%rdi)
 ; SSE-NEXT:    retq
 ;
diff --git a/llvm/test/CodeGen/X86/vselect-minmax.ll b/llvm/test/CodeGen/X86/vselect-minmax.ll
index 2352b6d6c0b8f..57f18714c11d0 100644
--- a/llvm/test/CodeGen/X86/vselect-minmax.ll
+++ b/llvm/test/CodeGen/X86/vselect-minmax.ll
@@ -10284,9 +10284,9 @@ define <8 x i64> @concat_smin_smax(<4 x i64> %a0, <4 x i64> %a1) {
 ; SSE2-LABEL: concat_smin_smax:
 ; SSE2:       # %bb.0:
 ; SSE2-NEXT:    movdqa {{.*#+}} xmm6 = [2147483648,2147483648]
-; SSE2-NEXT:    movdqa %xmm3, %xmm4
+; SSE2-NEXT:    movdqa %xmm2, %xmm4
 ; SSE2-NEXT:    pxor %xmm6, %xmm4
-; SSE2-NEXT:    movdqa %xmm1, %xmm5
+; SSE2-NEXT:    movdqa %xmm0, %xmm5
 ; SSE2-NEXT:    pxor %xmm6, %xmm5
 ; SSE2-NEXT:    movdqa %xmm5, %xmm7
 ; SSE2-NEXT:    pcmpgtd %xmm4, %xmm7
@@ -10297,13 +10297,13 @@ define <8 x i64> @concat_smin_smax(<4 x i64> %a0, <4 x i64> %a1) {
 ; SSE2-NEXT:    pshufd {{.*#+}} xmm4 = xmm7[1,1,3,3]
 ; SSE2-NEXT:    por %xmm5, %xmm4
 ; SSE2-NEXT:    movdqa %xmm4, %xmm7
-; SSE2-NEXT:    pandn %xmm1, %xmm7
-; SSE2-NEXT:    movdqa %xmm3, %xmm5
+; SSE2-NEXT:    pandn %xmm0, %xmm7
+; SSE2-NEXT:    movdqa %xmm2, %xmm5
 ; SSE2-NEXT:    pand %xmm4, %xmm5
 ; SSE2-NEXT:    por %xmm7, %xmm5
-; SSE2-NEXT:    movdqa %xmm2, %xmm7
+; SSE2-NEXT:    movdqa %xmm3, %xmm7
 ; SSE2-NEXT:    pxor %xmm6, %xmm7
-; SSE2-NEXT:    pxor %xmm0, %xmm6
+; SSE2-NEXT:    pxor %xmm1, %xmm6
 ; SSE2-NEXT:    movdqa %xmm6, %xmm8
 ; SSE2-NEXT:    pcmpgtd %xmm7, %xmm8
 ; SSE2-NEXT:    pshufd {{.*#+}} xmm9 = xmm8[0,0,2,2]
@@ -10313,42 +10313,41 @@ define <8 x i64> @concat_smin_smax(<4 x i64> %a0, <4 x i64> %a1) {
 ; SSE2-NEXT:    pshufd {{.*#+}} xmm6 = xmm8[1,1,3,3]
 ; SSE2-NEXT:    por %xmm7, %xmm6
 ; SSE2-NEXT:    movdqa %xmm6, %xmm7
-; SSE2-NEXT:    pandn %xmm2, %xmm7
+; SSE2-NEXT:    pandn %xmm1, %xmm7
 ; SSE2-NEXT:    movdqa %xmm6, %xmm8
-; SSE2-NEXT:    pandn %xmm0, %xmm8
-; SSE2-NEXT:    pand %xmm6, %xmm0
-; SSE2-NEXT:    por %xmm0, %xmm7
-; SSE2-NEXT:    pand %xmm4, %xmm1
-; SSE2-NEXT:    pandn %xmm3, %xmm4
-; SSE2-NEXT:    por %xmm1, %xmm4
-; SSE2-NEXT:    pand %xmm2, %xmm6
+; SSE2-NEXT:    pandn %xmm3, %xmm8
+; SSE2-NEXT:    pand %xmm6, %xmm3
+; SSE2-NEXT:    por %xmm3, %xmm7
+; SSE2-NEXT:    pand %xmm4, %xmm0
+; SSE2-NEXT:    pandn %xmm2, %xmm4
+; SSE2-NEXT:    por %xmm0, %xmm4
+; SSE2-NEXT:    pand %xmm1, %xmm6
 ; SSE2-NEXT:    por %xmm8, %xmm6
-; SSE2-NEXT:    movdqa %xmm6, %xmm0
-; SSE2-NEXT:    movdqa %xmm5, %xmm1
-; SSE2-NEXT:    movdqa %xmm7, %xmm2
-; SSE2-NEXT:    movdqa %xmm4, %xmm3
+; SSE2-NEXT:    movdqa %xmm5, %xmm0
+; SSE2-NEXT:    movdqa %xmm7, %xmm1
+; SSE2-NEXT:    movdqa %xmm4, %xmm2
+; SSE2-NEXT:    movdqa %xmm6, %xmm3
 ; SSE2-NEXT:    retq
 ;
 ; SSE4-LABEL: concat_smin_smax:
 ; SSE4:       # %bb.0:
 ; SSE4-NEXT:    movdqa %xmm0, %xmm4
-; SSE4-NEXT:    movdqa %xmm1, %xmm5
-; SSE4-NEXT:    pcmpgtq %xmm3, %xmm5
-; SSE4-NEXT:    movdqa %xmm1, %xmm6
+; SSE4-NEXT:    movdqa %xmm0, %xmm5
+; SSE4-NEXT:    pcmpgtq %xmm2, %xmm5
+; SSE4-NEXT:    movdqa %xmm0, %xmm6
 ; SSE4-NEXT:    movdqa %xmm5, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm3, %xmm6
-; SSE4-NEXT:    movdqa %xmm4, %xmm7
-; SSE4-NEXT:    pcmpgtq %xmm2, %xmm7
-; SSE4-NEXT:    movdqa %xmm2, %xmm8
+; SSE4-NEXT:    blendvpd %xmm0, %xmm2, %xmm6
+; SSE4-NEXT:    movdqa %xmm1, %xmm7
+; SSE4-NEXT:    pcmpgtq %xmm3, %xmm7
+; SSE4-NEXT:    movdqa %xmm1, %xmm8
 ; SSE4-NEXT:    movdqa %xmm7, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm4, %xmm8
+; SSE4-NEXT:    blendvpd %xmm0, %xmm3, %xmm8
 ; SSE4-NEXT:    movdqa %xmm5, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm1, %xmm3
+; SSE4-NEXT:    blendvpd %xmm0, %xmm4, %xmm2
 ; SSE4-NEXT:    movdqa %xmm7, %xmm0
-; SSE4-NEXT:    blendvpd %xmm0, %xmm2, %xmm4
-; SSE4-NEXT:    movapd %xmm4, %xmm0
-; SSE4-NEXT:    movapd %xmm6, %xmm1
-; SSE4-NEXT:    movapd %xmm8, %xmm2
+; SSE4-NEXT:    blendvpd %xmm0, %xmm1, %xmm3
+; SSE4-NEXT:    movapd %xmm6, %xmm0
+; SSE4-NEXT:    movapd %xmm8, %xmm1
 ; SSE4-NEXT:    retq
 ;
 ; AVX1-LABEL: concat_smin_smax:

>From 2c8588400a33f9eac1a2960e94f07748c596be6c Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Mon, 8 Jun 2026 13:36:07 +0800
Subject: [PATCH 06/15] convert all extract when first meet

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 125 ++++++++++++------
 llvm/test/CodeGen/X86/vector-narrow-binop.ll  |   4 +-
 2 files changed, 87 insertions(+), 42 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 2d60b15bf2fdb..b2ddeb5a8f5e7 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallBitVector.h"
@@ -27170,26 +27171,55 @@ SDValue DAGCombiner::visitVECTOR_INTERLEAVE(SDNode *N) {
   return CombineTo(N, &Ops);
 }
 
-// Helper that peeks through INSERT_SUBVECTOR/CONCAT_VECTORS to find
-// if the subvector can be sourced for free.
-static SDValue getSubVectorSrc(SDValue V, unsigned Index, EVT SubVT) {
-  if (V.getOpcode() == ISD::INSERT_SUBVECTOR &&
-      V.getOperand(1).getValueType() == SubVT &&
-      V.getConstantOperandAPInt(2) == Index) {
-    return V.getOperand(1);
+// Scan one wide operand's INSERT_SUBVECTOR chain (optionally rooted at a
+// CONCAT_VECTORS) a single time and fill in OpNo's source slot for the indices
+// we actually want, i.e. those already seeded in Srcs by the extract users.
+// Indices not in Srcs are ignored, so the chain is never recorded wholesale.
+// The outermost definition of an index wins; an insert that is not
+// SubVT-aligned can straddle two slots, so we stop there and leave deeper
+// indices unavailable.
+static void collectSubVectorSrcs(
+    SDValue V, EVT SubVT, unsigned OpNo,
+    SmallMapVector<unsigned, std::tuple<SDNode *, SDValue, SDValue>, 4> &Srcs) {
+  unsigned NumSubElts = SubVT.getVectorMinNumElements();
+  auto record = [&](unsigned Idx, SDValue Sub) {
+    auto It = Srcs.find(Idx);
+    if (It == Srcs.end())
+      return;
+    SDValue &Slot =
+        OpNo == 0 ? std::get<1>(It->second) : std::get<2>(It->second);
+    if (!Slot)
+      Slot = Sub;
+  };
+  while (V.getOpcode() == ISD::INSERT_SUBVECTOR &&
+         V.getOperand(1).getValueType() == SubVT) {
+    uint64_t InsIdx = V.getConstantOperandVal(2);
+    if ((InsIdx % NumSubElts) != 0)
+      return;
+    record(InsIdx, V.getOperand(1));
+    V = V.getOperand(0);
   }
   if (V.getOpcode() == ISD::CONCAT_VECTORS &&
-      V.getOperand(0).getValueType() == SubVT &&
-      (Index % SubVT.getVectorMinNumElements()) == 0) {
-    uint64_t SubIdx = Index / SubVT.getVectorMinNumElements();
-    return V.getOperand(SubIdx);
-  }
-  return SDValue();
-}
-
-static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
-                                              SDValue BinOp, unsigned Index,
-                                              const SDLoc &DL,
+      V.getOperand(0).getValueType() == SubVT)
+    for (unsigned I = 0, E = V.getNumOperands(); I != E; ++I)
+      record(I * NumSubElts, V.getOperand(I));
+}
+
+// Try to narrow a wide vector binop whose result is consumed *only* by
+// extract_subvector nodes that all extract the SAME narrow type. The pattern
+// is:
+//   extract (binop (insert/concat ..., X, Idx), (insert/concat ..., Y, Idx)),
+//   Idx
+// When every extract pulls the same SubVT out of both wide operands for free,
+// we replace the wide binop by one narrow binop per extract, eliminating all of
+// the inserts/extracts and the wide binop in a single combine:
+//   extract ..., Idx --> binop X, Y
+// We seed a map with the wanted extract indices, then scan each wide operand's
+// insert/concat chain exactly once to fill in the per-operand sources, so the
+// chains are never re-walked per extract. N is the extract currently being
+// combined: its narrow binop is returned for the caller to fold in, while the
+// sibling extracts are rewritten here so the wide binop is left with no users.
+static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
                                               SelectionDAG &DAG,
                                               bool LegalOperations) {
   const TargetLowering &TLI = DAG.getTargetLoweringInfo();
@@ -27201,32 +27231,48 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, EVT SubVT,
   SDValue Bop0 = BinOp.getOperand(0), Bop1 = BinOp.getOperand(1);
   if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType())
     return SDValue();
-  if (!TLI.isOperationLegalOrCustom(BinOpcode, SubVT, LegalOperations))
-    return SDValue();
-
-  SDValue Sub0 = getSubVectorSrc(Bop0, Index, SubVT);
-  SDValue Sub1 = getSubVectorSrc(Bop1, Index, SubVT);
-
-  // TODO: We could handle the case where only 1 operand is being inserted by
-  //       creating an extract of the other operand, but that requires checking
-  //       number of uses and/or costs.
-  if (!Sub0 || !Sub1)
-    return SDValue();
 
-  // Every other user of the wide binop must be an EXTRACT_SUBVECTOR that is
-  // still on the combiner worklist; otherwise the wide binop may remain needed.
+  // This fold only pays off when the wide binop disappears completely, so every
+  // user must be an extract_subvector. Require them all to extract N's type so
+  // a single chain scan serves every extract, and seed the source map by index.
+  EVT SubVT = N->getValueType(0);
+  SmallMapVector<unsigned, std::tuple<SDNode *, SDValue, SDValue>, 4> Extracts;
   for (SDNode *User : BinOp->users()) {
-    if (User == N)
-      continue;
     if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR ||
-        User->getCombinerWorklistIndex() < 0)
+        User->getValueType(0) != SubVT ||
+        (User->getCombinerWorklistIndex() < 0 && User != N))
       return SDValue();
+    Extracts[User->getConstantOperandVal(1)] =
+        std::make_tuple(User, SDValue(), SDValue());
   }
 
-  // We are inserting both operands of the wide binop only to extract back
-  // to the narrow vector size. Eliminate all of the insert/extract:
-  // ext (binop (ins ?, X, Index), (ins ?, Y, Index)), Index --> binop X, Y
-  return DAG.getNode(BinOpcode, DL, SubVT, Sub0, Sub1, BinOp->getFlags());
+  if (!TLI.isOperationLegalOrCustom(BinOpcode, SubVT, LegalOperations))
+    return SDValue();
+
+  // Scan each wide operand's chain once, filling the seeded indices' sources.
+  collectSubVectorSrcs(Bop0, SubVT, /*OpNo=*/0, Extracts);
+  collectSubVectorSrcs(Bop1, SubVT, /*OpNo=*/1, Extracts);
+
+  // Bail unless every wanted index was sourced for free from both operands, so
+  // we commit to rewriting all of the extracts or none.
+  for (auto &[Idx, Ext] : Extracts)
+    if (!std::get<1>(Ext) || !std::get<2>(Ext))
+      return SDValue();
+
+  // Replace each extract with a narrow binop over the matching subvectors:
+  // ext (binop (ins ?, X, Idx), (ins ?, Y, Idx)), Idx --> binop X, Y
+  // Build N's replacement first so node creation order (and thus scheduling)
+  // matches the combiner visiting N directly; rewrite the siblings in place.
+  SDValue Result = SDValue();
+  for (auto &[Idx, Entry] : Extracts) {
+    auto [Ext, Sub0, Sub1] = Entry;
+    SDValue Narrow = DAG.getNode(BinOpcode, SDLoc(Ext), SubVT, Sub0, Sub1,
+                                 BinOp->getFlags());
+    DAG.ReplaceAllUsesOfValueWith(SDValue(Ext, 0), Narrow);
+    if (Ext == N)
+      Result = Narrow;
+  }
+  return Result;
 }
 
 /// If we are extracting a subvector produced by a wide binary operator try
@@ -27238,8 +27284,7 @@ static SDValue narrowExtractedVectorBinOp(SDNode *N, EVT VT, SDValue Src,
   // TODO: Refactor with the caller (visitEXTRACT_SUBVECTOR), so we can share
   // some of these bailouts with other transforms.
 
-  if (SDValue V = narrowInsertExtractVectorBinOp(N, VT, Src, Index, DL, DAG,
-                                                 LegalOperations))
+  if (SDValue V = narrowInsertExtractVectorBinOp(N, Src, DAG, LegalOperations))
     return V;
 
   // We are looking for an optionally bitcasted wide vector binary operator
diff --git a/llvm/test/CodeGen/X86/vector-narrow-binop.ll b/llvm/test/CodeGen/X86/vector-narrow-binop.ll
index ad345213c1472..804f6c10e3ba4 100644
--- a/llvm/test/CodeGen/X86/vector-narrow-binop.ll
+++ b/llvm/test/CodeGen/X86/vector-narrow-binop.ll
@@ -180,8 +180,8 @@ define <4 x double> @fmul_v2f64(<2 x  double> %x, <2 x double> %y) {
 ;
 ; AVX512-LABEL: fmul_v2f64:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vunpckhpd {{.*#+}} xmm2 = xmm0[1],xmm1[1]
-; AVX512-NEXT:    vunpcklpd {{.*#+}} xmm0 = xmm1[0],xmm0[0]
+; AVX512-NEXT:    vunpcklpd {{.*#+}} xmm2 = xmm1[0],xmm0[0]
+; AVX512-NEXT:    vunpckhpd {{.*#+}} xmm0 = xmm0[1],xmm1[1]
 ; AVX512-NEXT:    vmulpd %xmm0, %xmm0, %xmm0
 ; AVX512-NEXT:    vfmadd231pd {{.*#+}} xmm0 = (xmm2 * xmm2) + xmm0
 ; AVX512-NEXT:    vshufpd {{.*#+}} xmm0 = xmm0[1,0]

>From fcfd4ad969b02e218e0db0e0bf3bf930b0cc4932 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Fri, 12 Jun 2026 17:51:18 +0800
Subject: [PATCH 07/15] add target specific block

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp      | 14 +++++++++++++-
 llvm/lib/Target/AArch64/AArch64ISelLowering.cpp    |  9 +++++++++
 llvm/lib/Target/AArch64/AArch64ISelLowering.h      |  2 ++
 .../CodeGen/RISCV/rvv/musttail-indirect-args.ll    |  4 ++--
 4 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 639ae05c0742c..d7629b75a78c4 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27235,13 +27235,16 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
 
   EVT VecVT = BinOp.getValueType();
   SDValue Bop0 = BinOp.getOperand(0), Bop1 = BinOp.getOperand(1);
-  if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType())
+  if (VecVT != Bop0.getValueType() || VecVT != Bop1.getValueType() ||
+      VecVT.isScalableVT())
     return SDValue();
 
   // This fold only pays off when the wide binop disappears completely, so every
   // user must be an extract_subvector. Require them all to extract N's type so
   // a single chain scan serves every extract, and seed the source map by index.
   EVT SubVT = N->getValueType(0);
+  if (VecVT.getSizeInBits() <= SubVT.getSizeInBits())
+    return SDValue();
   SmallMapVector<unsigned, std::tuple<SDNode *, SDValue, SDValue>, 4> Extracts;
   for (SDNode *User : BinOp->users()) {
     if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR ||
@@ -27265,6 +27268,15 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
     if (!std::get<1>(Ext) || !std::get<2>(Ext))
       return SDValue();
 
+  if (TLI.isTypeLegal(VecVT)) {
+    bool AllExtractsCheap = true;
+    for (auto &[Idx, Ext] : Extracts)
+      AllExtractsCheap &= TLI.isExtractSubvectorCheap(SubVT, VecVT, Idx);
+    if (AllExtractsCheap &&
+        !TLI.isNarrowingProfitable(BinOp.getNode(), VecVT, SubVT))
+      return SDValue();
+  }
+
   // Replace each extract with a narrow binop over the matching subvectors:
   // ext (binop (ins ?, X, Idx), (ins ?, Y, Idx)), Idx --> binop X, Y
   // Build N's replacement first so node creation order (and thus scheduling)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 57a2d73e00f57..0738f07404af6 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -19676,6 +19676,15 @@ bool AArch64TargetLowering::isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
   return (Index == 0 || Index == ResVT.getVectorMinNumElements());
 }
 
+bool AArch64TargetLowering::isNarrowingProfitable(SDNode *N, EVT SrcVT,
+                                                  EVT DestVT) const {
+  // Splitting a wide vector binop into narrower ones removes the wide operand
+  // construction (a concatenation), so prefer narrowing for vectors. Scalar
+  // bit-width narrowing is not generally profitable on AArch64, so keep the
+  // conservative default there.
+  return SrcVT.isVector() && isBinOp(N->getOpcode());
+}
+
 bool AArch64TargetLowering::shouldOptimizeMulOverflowWithZeroHighBits(
     LLVMContext &Context, EVT VT) const {
   if (getTypeAction(Context, VT) != TypeExpandInteger)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 7e4c4e1ba25ff..522d761f0f1cc 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -331,6 +331,8 @@ class AArch64TargetLowering : public TargetLowering {
   bool isExtractSubvectorCheap(EVT ResVT, EVT SrcVT,
                                unsigned Index) const override;
 
+  bool isNarrowingProfitable(SDNode *N, EVT SrcVT, EVT DestVT) const override;
+
   bool shouldFormOverflowOp(unsigned Opcode, EVT VT,
                             bool MathUsed) const override {
     // Using overflow ops for overflow checks only should beneficial on
diff --git a/llvm/test/CodeGen/RISCV/rvv/musttail-indirect-args.ll b/llvm/test/CodeGen/RISCV/rvv/musttail-indirect-args.ll
index 082786f35cb0f..5aa500b018b46 100644
--- a/llvm/test/CodeGen/RISCV/rvv/musttail-indirect-args.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/musttail-indirect-args.ll
@@ -58,9 +58,9 @@ define <vscale x 32 x i32> @caller_musttail_scalable_mixed(<vscale x 32 x i32> %
 ; CHECK-NEXT:    vl8re32.v v0, (a1)
 ; CHECK-NEXT:    vsetvli a2, zero, e32, m8, ta, ma
 ; CHECK-NEXT:    vadd.vv v24, v8, v24
+; CHECK-NEXT:    vadd.vv v0, v16, v0
 ; CHECK-NEXT:    vs8r.v v24, (a0)
-; CHECK-NEXT:    vadd.vv v24, v16, v0
-; CHECK-NEXT:    vs8r.v v24, (a1)
+; CHECK-NEXT:    vs8r.v v0, (a1)
 ; CHECK-NEXT:    tail callee_musttail_scalable
   %s = add <vscale x 32 x i32> %x, %y
   %r = musttail call <vscale x 32 x i32> @callee_musttail_scalable(<vscale x 32 x i32> %x, <vscale x 32 x i32> %s)

>From 463e240e0bd9598d67165126f9d8cb501d0b9b5f Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Mon, 15 Jun 2026 08:46:37 +0800
Subject: [PATCH 08/15] fix tests

---
 llvm/test/CodeGen/AMDGPU/maximumnum.ll | 4 ++--
 llvm/test/CodeGen/AMDGPU/minimumnum.ll | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/maximumnum.ll b/llvm/test/CodeGen/AMDGPU/maximumnum.ll
index 9b7c8a4e3f314..210a640942b97 100644
--- a/llvm/test/CodeGen/AMDGPU/maximumnum.ll
+++ b/llvm/test/CodeGen/AMDGPU/maximumnum.ll
@@ -5133,11 +5133,11 @@ define <3 x half> @v_maximumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX1251-SDAG:       ; %bb.0:
 ; GFX1251-SDAG-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX1251-SDAG-NEXT:    s_wait_kmcnt 0x0
-; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v2, v2, v2
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v0
+; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v1
-; GFX1251-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1251-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v2
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v3
 ; GFX1251-SDAG-NEXT:    s_set_pc_i64 s[30:31]
diff --git a/llvm/test/CodeGen/AMDGPU/minimumnum.ll b/llvm/test/CodeGen/AMDGPU/minimumnum.ll
index e4107760310be..33c8662a2153e 100644
--- a/llvm/test/CodeGen/AMDGPU/minimumnum.ll
+++ b/llvm/test/CodeGen/AMDGPU/minimumnum.ll
@@ -4883,11 +4883,11 @@ define <3 x half> @v_minimumnum_v3f16(<3 x half> %x, <3 x half> %y) #1 {
 ; GFX1251-SDAG:       ; %bb.0:
 ; GFX1251-SDAG-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX1251-SDAG-NEXT:    s_wait_kmcnt 0x0
-; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v2, v2, v2
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v0, v0, v0
+; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v3, v3, v3
 ; GFX1251-SDAG-NEXT:    v_pk_max_num_f16 v1, v1, v1
-; GFX1251-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1251-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_3) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX1251-SDAG-NEXT:    v_pk_min_num_f16 v0, v0, v2
 ; GFX1251-SDAG-NEXT:    v_pk_min_num_f16 v1, v1, v3
 ; GFX1251-SDAG-NEXT:    s_set_pc_i64 s[30:31]

>From 5a654d88fa9fd3e3d56e1eb9ce2ee5b8aaf9238a Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Wed, 17 Jun 2026 09:55:11 +0800
Subject: [PATCH 09/15] fix test

---
 llvm/test/CodeGen/AMDGPU/usubsat.ll | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/usubsat.ll b/llvm/test/CodeGen/AMDGPU/usubsat.ll
index 7b0a019a710f0..4b8f7cc180eb6 100644
--- a/llvm/test/CodeGen/AMDGPU/usubsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/usubsat.ll
@@ -683,10 +683,10 @@ define <3 x i16> @s_usubsat_v3i16(<3 x i16> inreg %lhs, <3 x i16> inreg %rhs) {
 ; GFX9-LABEL: s_usubsat_v3i16:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    v_mov_b32_e32 v0, s19
-; GFX9-NEXT:    v_pk_sub_u16 v1, s17, v0 clamp
 ; GFX9-NEXT:    v_mov_b32_e32 v0, s18
+; GFX9-NEXT:    v_mov_b32_e32 v1, s19
 ; GFX9-NEXT:    v_pk_sub_u16 v0, s16, v0 clamp
+; GFX9-NEXT:    v_pk_sub_u16 v1, s17, v1 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: s_usubsat_v3i16:

>From 9b8a71d90b8c324b608e14a23278816005d09375 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Mon, 22 Jun 2026 16:15:04 +0800
Subject: [PATCH 10/15] fix test case

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 75 +++++++++++++------
 1 file changed, 52 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 83ddf78dee0e6..5b0a1dca212d2 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27235,11 +27235,8 @@ static void collectSubVectorSrcs(
     SmallMapVector<unsigned, std::tuple<SDNode *, SDValue, SDValue>, 4> &Srcs) {
   unsigned NumSubElts = SubVT.getVectorMinNumElements();
   auto record = [&](unsigned Idx, SDValue Sub) {
-    auto It = Srcs.find(Idx);
-    if (It == Srcs.end())
-      return;
-    SDValue &Slot =
-        OpNo == 0 ? std::get<1>(It->second) : std::get<2>(It->second);
+    std::tuple<SDNode *, SDValue, SDValue> &Ext = Srcs[Idx];
+    SDValue &Slot = OpNo == 0 ? std::get<1>(Ext) : std::get<2>(Ext);
     if (!Slot)
       Slot = Sub;
   };
@@ -27291,33 +27288,63 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
   EVT SubVT = N->getValueType(0);
   if (VecVT.getSizeInBits() <= SubVT.getSizeInBits())
     return SDValue();
+
   SmallMapVector<unsigned, std::tuple<SDNode *, SDValue, SDValue>, 4> Extracts;
+  // Scan each wide operand's chain once, filling the seeded indices' sources.
+  collectSubVectorSrcs(Bop0, SubVT, /*OpNo=*/0, Extracts);
+  collectSubVectorSrcs(Bop1, SubVT, /*OpNo=*/1, Extracts);
+
+  bool HasNonZeroExt = false;
+  bool HasNonExtUser = false;
   for (SDNode *User : BinOp->users()) {
-    if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR ||
-        User->getValueType(0) != SubVT ||
+    if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR) {
+      HasNonExtUser = true;
+      continue;
+    }
+    if (User->getValueType(0) != SubVT ||
         (User->getCombinerWorklistIndex() < 0 && User != N))
       return SDValue();
-    Extracts[User->getConstantOperandVal(1)] =
-        std::make_tuple(User, SDValue(), SDValue());
+    unsigned Idx = User->getConstantOperandVal(1);
+    auto It = Extracts.find(Idx);
+    if (It == Extracts.end() || !std::get<1>(It->second) ||
+        !std::get<2>(It->second))
+      return SDValue();
+    SDNode *&ExtSubVec = std::get<0>(It->second);
+    if (!ExtSubVec) {
+      ExtSubVec = User;
+      if (Idx != 0)
+        HasNonZeroExt = true;
+    }
   }
 
   if (!TLI.isOperationLegalOrCustom(BinOpcode, SubVT, LegalOperations))
     return SDValue();
 
-  // Scan each wide operand's chain once, filling the seeded indices' sources.
-  collectSubVectorSrcs(Bop0, SubVT, /*OpNo=*/0, Extracts);
-  collectSubVectorSrcs(Bop1, SubVT, /*OpNo=*/1, Extracts);
-
-  // Bail unless every wanted index was sourced for free from both operands, so
-  // we commit to rewriting all of the extracts or none.
-  for (auto &[Idx, Ext] : Extracts)
-    if (!std::get<1>(Ext) || !std::get<2>(Ext))
+  // Narrow for [SubVT, 0/undef,...,0/undef]: when the wide binop also has a
+  // non-extract user it survives, so narrowing only pays off if it folds for
+  // free to concat(narrow binop, 0/undef, ...). That holds when the sole
+  // extract is lane 0 (HasNonZeroExt rejects multi-real-lane cases such as
+  // [a,b,0,0,c,d]) and every other lane is 0/undef in both operands; otherwise
+  // a lane carries real data the wide binop must still compute. `V &&` keeps
+  // IsZeroOrUndef null-safe for lanes that were never sourced.
+  auto IsZeroOrUndef = [](SDValue V) {
+    return V && (V.isUndef() || ISD::isBuildVectorAllZeros(V.getNode()) ||
+                 isNullOrNullSplat(V));
+  };
+  if (HasNonExtUser) {
+    if (HasNonZeroExt)
       return SDValue();
+    for (auto &[Idx, Entry] : Extracts)
+      if (Idx != 0 && (!IsZeroOrUndef(std::get<1>(Entry)) ||
+                       !IsZeroOrUndef(std::get<2>(Entry))))
+        return SDValue();
+  }
 
   if (TLI.isTypeLegal(VecVT)) {
     bool AllExtractsCheap = true;
     for (auto &[Idx, Ext] : Extracts)
-      AllExtractsCheap &= TLI.isExtractSubvectorCheap(SubVT, VecVT, Idx);
+      if (std::get<0>(Ext)) // Only lanes actually consumed by an extract.
+        AllExtractsCheap &= TLI.isExtractSubvectorCheap(SubVT, VecVT, Idx);
     if (AllExtractsCheap &&
         !TLI.isNarrowingProfitable(BinOp.getNode(), VecVT, SubVT))
       return SDValue();
@@ -27330,11 +27357,13 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
   SDValue Result = SDValue();
   for (auto &[Idx, Entry] : Extracts) {
     auto [Ext, Sub0, Sub1] = Entry;
-    SDValue Narrow = DAG.getNode(BinOpcode, SDLoc(Ext), SubVT, Sub0, Sub1,
-                                 BinOp->getFlags());
-    DAG.ReplaceAllUsesOfValueWith(SDValue(Ext, 0), Narrow);
-    if (Ext == N)
-      Result = Narrow;
+    if (Ext) {
+      SDValue Narrow = DAG.getNode(BinOpcode, SDLoc(Ext), SubVT, Sub0, Sub1,
+                                   BinOp->getFlags());
+      DAG.ReplaceAllUsesOfValueWith(SDValue(Ext, 0), Narrow);
+      if (Ext == N)
+        Result = Narrow;
+    }
   }
   return Result;
 }

>From 8e535fd4ebfc91340eb34de4a2342a3a12b817d0 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Tue, 30 Jun 2026 12:27:51 +0800
Subject: [PATCH 11/15] fix comments

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 97 +++++++++++--------
 1 file changed, 56 insertions(+), 41 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 366daf643cdd8..a0b7866f311f8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27241,19 +27241,22 @@ SDValue DAGCombiner::visitVECTOR_INTERLEAVE(SDNode *N) {
 }
 
 // Scan one wide operand's INSERT_SUBVECTOR chain (optionally rooted at a
-// CONCAT_VECTORS) a single time and fill in OpNo's source slot for the indices
-// we actually want, i.e. those already seeded in Srcs by the extract users.
-// Indices not in Srcs are ignored, so the chain is never recorded wholesale.
-// The outermost definition of an index wins; an insert that is not
-// SubVT-aligned can straddle two slots, so we stop there and leave deeper
-// indices unavailable.
+// CONCAT_VECTORS) a single time and fill in OpNo's source for each SubVT-sized
+// slot. Slots are indexed by their subvector position, so the chain is never
+// recorded wholesale. Each slot is a tuple of the extract_subvector user (if
+// any) and the matching subvector source from each wide operand; a slot whose
+// two sources are both null was never seen on either chain (i.e. it is undef).
+// The outermost definition of a slot wins; an insert that is not SubVT-aligned
+// can straddle two slots, so we stop there and leave deeper slots unavailable.
 static void collectSubVectorSrcs(
     SDValue V, EVT SubVT, unsigned OpNo,
-    SmallMapVector<unsigned, std::tuple<SDNode *, SDValue, SDValue>, 4> &Srcs) {
+    MutableArrayRef<std::tuple<SDNode *, SDValue, SDValue>> Slots) {
   unsigned NumSubElts = SubVT.getVectorMinNumElements();
-  auto record = [&](unsigned Idx, SDValue Sub) {
-    std::tuple<SDNode *, SDValue, SDValue> &Ext = Srcs[Idx];
-    SDValue &Slot = OpNo == 0 ? std::get<1>(Ext) : std::get<2>(Ext);
+  auto record = [&](unsigned Part, SDValue Sub) {
+    if (Part >= Slots.size())
+      return;
+    SDValue &Slot =
+        OpNo == 0 ? std::get<1>(Slots[Part]) : std::get<2>(Slots[Part]);
     if (!Slot)
       Slot = Sub;
   };
@@ -27262,13 +27265,13 @@ static void collectSubVectorSrcs(
     uint64_t InsIdx = V.getConstantOperandVal(2);
     if ((InsIdx % NumSubElts) != 0)
       return;
-    record(InsIdx, V.getOperand(1));
+    record(InsIdx / NumSubElts, V.getOperand(1));
     V = V.getOperand(0);
   }
   if (V.getOpcode() == ISD::CONCAT_VECTORS &&
       V.getOperand(0).getValueType() == SubVT)
     for (unsigned I = 0, E = V.getNumOperands(); I != E; ++I)
-      record(I * NumSubElts, V.getOperand(I));
+      record(I, V.getOperand(I));
 }
 
 // Try to narrow a wide vector binop whose result is consumed *only* by
@@ -27280,11 +27283,12 @@ static void collectSubVectorSrcs(
 // we replace the wide binop by one narrow binop per extract, eliminating all of
 // the inserts/extracts and the wide binop in a single combine:
 //   extract ..., Idx --> binop X, Y
-// We seed a map with the wanted extract indices, then scan each wide operand's
-// insert/concat chain exactly once to fill in the per-operand sources, so the
-// chains are never re-walked per extract. N is the extract currently being
-// combined: its narrow binop is returned for the caller to fold in, while the
-// sibling extracts are rewritten here so the wide binop is left with no users.
+// We use a flat per-slot table indexed by subvector position, then scan each
+// wide operand's insert/concat chain exactly once to fill in the per-operand
+// sources, so the chains are never re-walked per extract. N is the extract
+// currently being combined: its narrow binop is returned for the caller to
+// fold in, while the sibling extracts are rewritten here so the wide binop is
+// left with no users.
 static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
                                               SelectionDAG &DAG,
                                               bool LegalOperations) {
@@ -27301,15 +27305,22 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
 
   // This fold only pays off when the wide binop disappears completely, so every
   // user must be an extract_subvector. Require them all to extract N's type so
-  // a single chain scan serves every extract, and seed the source map by index.
+  // a single chain scan serves every extract.
   EVT SubVT = N->getValueType(0);
   if (VecVT.getSizeInBits() <= SubVT.getSizeInBits())
     return SDValue();
 
-  SmallMapVector<unsigned, std::tuple<SDNode *, SDValue, SDValue>, 4> Extracts;
-  // Scan each wide operand's chain once, filling the seeded indices' sources.
-  collectSubVectorSrcs(Bop0, SubVT, /*OpNo=*/0, Extracts);
-  collectSubVectorSrcs(Bop1, SubVT, /*OpNo=*/1, Extracts);
+  if (!TLI.isOperationLegalOrCustom(BinOpcode, SubVT, LegalOperations))
+    return SDValue();
+
+  // The wide binop splits into a small, dense set of SubVT-sized slots, so a
+  // flat table indexed by subvector position is all we need.
+  unsigned NumSubElts = SubVT.getVectorMinNumElements();
+  unsigned NumParts = VecVT.getVectorMinNumElements() / NumSubElts;
+  SmallVector<std::tuple<SDNode *, SDValue, SDValue>, 4> Slots(NumParts);
+  // Scan each wide operand's chain once, filling each slot's source.
+  collectSubVectorSrcs(Bop0, SubVT, /*OpNo=*/0, Slots);
+  collectSubVectorSrcs(Bop1, SubVT, /*OpNo=*/1, Slots);
 
   bool HasNonZeroExt = false;
   bool HasNonExtUser = false;
@@ -27322,11 +27333,12 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
         (User->getCombinerWorklistIndex() < 0 && User != N))
       return SDValue();
     unsigned Idx = User->getConstantOperandVal(1);
-    auto It = Extracts.find(Idx);
-    if (It == Extracts.end() || !std::get<1>(It->second) ||
-        !std::get<2>(It->second))
+    if (Idx % NumSubElts != 0 || Idx / NumSubElts >= NumParts)
+      return SDValue();
+    auto &Slot = Slots[Idx / NumSubElts];
+    if (!std::get<1>(Slot) || !std::get<2>(Slot))
       return SDValue();
-    SDNode *&ExtSubVec = std::get<0>(It->second);
+    SDNode *&ExtSubVec = std::get<0>(Slot);
     if (!ExtSubVec) {
       ExtSubVec = User;
       if (Idx != 0)
@@ -27334,34 +27346,37 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
     }
   }
 
-  if (!TLI.isOperationLegalOrCustom(BinOpcode, SubVT, LegalOperations))
-    return SDValue();
-
   // Narrow for [SubVT, 0/undef,...,0/undef]: when the wide binop also has a
   // non-extract user it survives, so narrowing only pays off if it folds for
   // free to concat(narrow binop, 0/undef, ...). That holds when the sole
   // extract is lane 0 (HasNonZeroExt rejects multi-real-lane cases such as
   // [a,b,0,0,c,d]) and every other lane is 0/undef in both operands; otherwise
-  // a lane carries real data the wide binop must still compute. `V &&` keeps
-  // IsZeroOrUndef null-safe for lanes that were never sourced.
+  // a lane carries real data the wide binop must still compute. A slot with
+  // both sources null was never sourced (i.e. undef) and is skipped; a slot
+  // sourced on only one operand has a null source there which is not provably
+  // 0/undef, so `V &&` makes IsZeroOrUndef reject it.
   auto IsZeroOrUndef = [](SDValue V) {
-    return V && (V.isUndef() || ISD::isBuildVectorAllZeros(V.getNode()) ||
-                 isNullOrNullSplat(V));
+    return V && (V.isUndef() || isNullOrNullSplat(V, /*AllowUndefs=*/true));
   };
   if (HasNonExtUser) {
     if (HasNonZeroExt)
       return SDValue();
-    for (auto &[Idx, Entry] : Extracts)
-      if (Idx != 0 && (!IsZeroOrUndef(std::get<1>(Entry)) ||
-                       !IsZeroOrUndef(std::get<2>(Entry))))
+    for (unsigned Part = 1; Part < NumParts; ++Part) {
+      auto &Slot = Slots[Part];
+      if (!std::get<1>(Slot) && !std::get<2>(Slot))
+        continue;
+      if (!IsZeroOrUndef(std::get<1>(Slot)) ||
+          !IsZeroOrUndef(std::get<2>(Slot)))
         return SDValue();
+    }
   }
 
   if (TLI.isTypeLegal(VecVT)) {
     bool AllExtractsCheap = true;
-    for (auto &[Idx, Ext] : Extracts)
-      if (std::get<0>(Ext)) // Only lanes actually consumed by an extract.
-        AllExtractsCheap &= TLI.isExtractSubvectorCheap(SubVT, VecVT, Idx);
+    for (unsigned Part = 0; Part < NumParts; ++Part)
+      if (std::get<0>(Slots[Part])) // Only lanes actually consumed by extract.
+        AllExtractsCheap &=
+            TLI.isExtractSubvectorCheap(SubVT, VecVT, Part * NumSubElts);
     if (AllExtractsCheap &&
         !TLI.isNarrowingProfitable(BinOp.getNode(), VecVT, SubVT))
       return SDValue();
@@ -27372,8 +27387,8 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
   // Build N's replacement first so node creation order (and thus scheduling)
   // matches the combiner visiting N directly; rewrite the siblings in place.
   SDValue Result = SDValue();
-  for (auto &[Idx, Entry] : Extracts) {
-    auto [Ext, Sub0, Sub1] = Entry;
+  for (auto &Slot : Slots) {
+    auto [Ext, Sub0, Sub1] = Slot;
     if (Ext) {
       SDValue Narrow = DAG.getNode(BinOpcode, SDLoc(Ext), SubVT, Sub0, Sub1,
                                    BinOp->getFlags());

>From a47f976ebb911cb117968b9ace7115225f6ad655 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Tue, 30 Jun 2026 15:05:54 +0800
Subject: [PATCH 12/15] fix comments

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 22 ++++++++-----------
 .../extract-subvector-binop-wide-use.ll       |  2 +-
 2 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index c114932618dea..8cb0ee151eece 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27326,6 +27326,7 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
 
   bool HasNonZeroExt = false;
   bool HasNonExtUser = false;
+  bool AllExtractsCheap = true;
   for (SDNode *User : BinOp->users()) {
     if (User->getOpcode() != ISD::EXTRACT_SUBVECTOR) {
       HasNonExtUser = true;
@@ -27343,11 +27344,18 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
     SDNode *&ExtSubVec = std::get<0>(Slot);
     if (!ExtSubVec) {
       ExtSubVec = User;
+      AllExtractsCheap &= TLI.isExtractSubvectorCheap(SubVT, VecVT, Idx);
       if (Idx != 0)
         HasNonZeroExt = true;
+    } else {
+      llvm_unreachable("Duplicate extract subvector");
     }
   }
 
+  if (TLI.isTypeLegal(VecVT) && AllExtractsCheap &&
+      !TLI.isNarrowingProfitable(BinOp.getNode(), VecVT, SubVT))
+    return SDValue();
+
   // Narrow for [SubVT, 0/undef,...,0/undef]: when the wide binop also has a
   // non-extract user it survives, so narrowing only pays off if it folds for
   // free to concat(narrow binop, 0/undef, ...). That holds when the sole
@@ -27373,24 +27381,12 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
     }
   }
 
-  if (TLI.isTypeLegal(VecVT)) {
-    bool AllExtractsCheap = true;
-    for (unsigned Part = 0; Part < NumParts; ++Part)
-      if (std::get<0>(Slots[Part])) // Only lanes actually consumed by extract.
-        AllExtractsCheap &=
-            TLI.isExtractSubvectorCheap(SubVT, VecVT, Part * NumSubElts);
-    if (AllExtractsCheap &&
-        !TLI.isNarrowingProfitable(BinOp.getNode(), VecVT, SubVT))
-      return SDValue();
-  }
-
   // Replace each extract with a narrow binop over the matching subvectors:
   // ext (binop (ins ?, X, Idx), (ins ?, Y, Idx)), Idx --> binop X, Y
   // Build N's replacement first so node creation order (and thus scheduling)
   // matches the combiner visiting N directly; rewrite the siblings in place.
   SDValue Result = SDValue();
-  for (auto &Slot : Slots) {
-    auto [Ext, Sub0, Sub1] = Slot;
+  for (auto [Ext, Sub0, Sub1] : Slots) {
     if (Ext) {
       SDValue Narrow = DAG.getNode(BinOpcode, SDLoc(Ext), SubVT, Sub0, Sub1,
                                    BinOp->getFlags());
diff --git a/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll b/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
index c4dcf7c4405b3..b97e0891b84d4 100644
--- a/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
+++ b/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 ; RUN: llc -mtriple=amdgcn -mcpu=gfx1250 < %s | FileCheck --check-prefix=GFX1250 %s
 
 define <4 x i32> @insert_extract_and_inreg_v4i32(ptr addrspace(1) inreg %out_sub, <2 x i32> inreg %a, <2 x i32> inreg %b) {

>From 3b9a88c67029b7b553e0829c1ef1c8ec5ffed75b Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Tue, 30 Jun 2026 17:00:43 +0800
Subject: [PATCH 13/15] fix comments

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 8cb0ee151eece..3c44342801446 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27338,10 +27338,9 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
     unsigned Idx = User->getConstantOperandVal(1);
     if (Idx % NumSubElts != 0 || Idx / NumSubElts >= NumParts)
       return SDValue();
-    auto &Slot = Slots[Idx / NumSubElts];
-    if (!std::get<1>(Slot) || !std::get<2>(Slot))
+    auto &[ExtSubVec, Sub0, Sub1] = Slots[Idx / NumSubElts];
+    if (!Sub0 || !Sub1)
       return SDValue();
-    SDNode *&ExtSubVec = std::get<0>(Slot);
     if (!ExtSubVec) {
       ExtSubVec = User;
       AllExtractsCheap &= TLI.isExtractSubvectorCheap(SubVT, VecVT, Idx);
@@ -27371,12 +27370,10 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
   if (HasNonExtUser) {
     if (HasNonZeroExt)
       return SDValue();
-    for (unsigned Part = 1; Part < NumParts; ++Part) {
-      auto &Slot = Slots[Part];
-      if (!std::get<1>(Slot) && !std::get<2>(Slot))
+    for (auto &[Ext, Sub0, Sub1] : drop_begin(Slots)) {
+      if (!Sub0 && !Sub1)
         continue;
-      if (!IsZeroOrUndef(std::get<1>(Slot)) ||
-          !IsZeroOrUndef(std::get<2>(Slot)))
+      if (!IsZeroOrUndef(Sub0) || !IsZeroOrUndef(Sub1))
         return SDValue();
     }
   }

>From 73be51e68ea702bc6515887f80dac838a6727a7b Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Thu, 2 Jul 2026 12:02:29 +0800
Subject: [PATCH 14/15] fix comments

---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 25 ++++++++++++-------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 3c44342801446..0f4e85095d7cd 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27262,6 +27262,13 @@ static void collectSubVectorSrcs(
     if (!Slot)
       Slot = Sub;
   };
+  if (V.getOpcode() == ISD::CONCAT_VECTORS &&
+      V.getOperand(0).getValueType() == SubVT) {
+    for (unsigned I = 0, E = V.getNumOperands(); I != E; ++I)
+      record(I, V.getOperand(I));
+    return;
+  }
+
   while (V.getOpcode() == ISD::INSERT_SUBVECTOR &&
          V.getOperand(1).getValueType() == SubVT) {
     uint64_t InsIdx = V.getConstantOperandVal(2);
@@ -27270,17 +27277,19 @@ static void collectSubVectorSrcs(
     record(InsIdx / NumSubElts, V.getOperand(1));
     V = V.getOperand(0);
   }
-  if (V.getOpcode() == ISD::CONCAT_VECTORS &&
-      V.getOperand(0).getValueType() == SubVT)
-    for (unsigned I = 0, E = V.getNumOperands(); I != E; ++I)
-      record(I, V.getOperand(I));
 }
 
 // Try to narrow a wide vector binop whose result is consumed *only* by
-// extract_subvector nodes that all extract the SAME narrow type. The pattern
-// is:
+// extract_subvector nodes that all extract the SAME narrow type SubVT. The
+// pattern, for each extract at index Idx, is:
 //   extract (binop (insert/concat ..., X, Idx), (insert/concat ..., Y, Idx)),
 //   Idx
+// Within one such match the two inserts and the extract share the same Idx, and
+// across the different extracts the indices are subvector-aligned and strided
+// by the subvector element count: Idx is a multiple of
+// SubVT.getVectorNumElements() (0, NumSubElts, 2*NumSubElts, ...), so each
+// extract pulls out a distinct, non-overlapping SubVT-sized slot.
+// Overlapping/unaligned indices (e.g. extract lanes 0-1 then 1-2) are rejected.
 // When every extract pulls the same SubVT out of both wide operands for free,
 // we replace the wide binop by one narrow binop per extract, eliminating all of
 // the inserts/extracts and the wide binop in a single combine:
@@ -27371,8 +27380,6 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
     if (HasNonZeroExt)
       return SDValue();
     for (auto &[Ext, Sub0, Sub1] : drop_begin(Slots)) {
-      if (!Sub0 && !Sub1)
-        continue;
       if (!IsZeroOrUndef(Sub0) || !IsZeroOrUndef(Sub1))
         return SDValue();
     }
@@ -27382,7 +27389,7 @@ static SDValue narrowInsertExtractVectorBinOp(SDNode *N, SDValue BinOp,
   // ext (binop (ins ?, X, Idx), (ins ?, Y, Idx)), Idx --> binop X, Y
   // Build N's replacement first so node creation order (and thus scheduling)
   // matches the combiner visiting N directly; rewrite the siblings in place.
-  SDValue Result = SDValue();
+  SDValue Result;
   for (auto [Ext, Sub0, Sub1] : Slots) {
     if (Ext) {
       SDValue Narrow = DAG.getNode(BinOpcode, SDLoc(Ext), SubVT, Sub0, Sub1,

>From 71785cadfc9c4e7e73421c46f7829a59041207e9 Mon Sep 17 00:00:00 2001
From: shore <372660931 at qq.com>
Date: Thu, 2 Jul 2026 12:07:20 +0800
Subject: [PATCH 15/15] fix comments

---
 .../extract-subvector-binop-wide-use.ll       | 53 +++++++++++++++++--
 1 file changed, 48 insertions(+), 5 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll b/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
index b97e0891b84d4..6e690238c7007 100644
--- a/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
+++ b/llvm/test/CodeGen/AMDGPU/extract-subvector-binop-wide-use.ll
@@ -1,9 +1,56 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=amdgcn -mcpu=gfx900 < %s | FileCheck --check-prefix=GFX9 %s
+; RUN: llc -mtriple=amdgcn -mcpu=gfx1030 < %s | FileCheck --check-prefix=GFX10 %s
+; RUN: llc -mtriple=amdgcn -mcpu=gfx1100 < %s | FileCheck --check-prefix=GFX11 %s
 ; RUN: llc -mtriple=amdgcn -mcpu=gfx1250 < %s | FileCheck --check-prefix=GFX1250 %s
 
 define <4 x i32> @insert_extract_and_inreg_v4i32(ptr addrspace(1) inreg %out_sub, <2 x i32> inreg %a, <2 x i32> inreg %b) {
+; GFX9-LABEL: insert_extract_and_inreg_v4i32:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    s_and_b32 s4, s19, s21
+; GFX9-NEXT:    s_and_b32 s5, s18, s20
+; GFX9-NEXT:    v_mov_b32_e32 v0, s5
+; GFX9-NEXT:    v_mov_b32_e32 v1, s4
+; GFX9-NEXT:    v_mov_b32_e32 v2, 0
+; GFX9-NEXT:    global_store_dwordx2 v2, v[0:1], s[16:17]
+; GFX9-NEXT:    v_mov_b32_e32 v0, 0
+; GFX9-NEXT:    v_mov_b32_e32 v1, 0
+; GFX9-NEXT:    v_mov_b32_e32 v2, s5
+; GFX9-NEXT:    v_mov_b32_e32 v3, s4
+; GFX9-NEXT:    s_waitcnt vmcnt(0)
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: insert_extract_and_inreg_v4i32:
+; GFX10:       ; %bb.0:
+; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT:    s_and_b32 s4, s18, s20
+; GFX10-NEXT:    s_and_b32 s5, s19, s21
+; GFX10-NEXT:    v_mov_b32_e32 v0, s4
+; GFX10-NEXT:    v_mov_b32_e32 v1, s5
+; GFX10-NEXT:    v_mov_b32_e32 v2, 0
+; GFX10-NEXT:    v_mov_b32_e32 v3, s5
+; GFX10-NEXT:    global_store_dwordx2 v2, v[0:1], s[16:17]
+; GFX10-NEXT:    v_mov_b32_e32 v0, 0
+; GFX10-NEXT:    v_mov_b32_e32 v1, 0
+; GFX10-NEXT:    v_mov_b32_e32 v2, s4
+; GFX10-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: insert_extract_and_inreg_v4i32:
+; GFX11:       ; %bb.0:
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    s_and_b32 s2, s2, s16
+; GFX11-NEXT:    s_and_b32 s3, s3, s17
+; GFX11-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX11-NEXT:    v_dual_mov_b32 v0, s2 :: v_dual_mov_b32 v1, s3
+; GFX11-NEXT:    v_dual_mov_b32 v2, 0 :: v_dual_mov_b32 v3, s3
+; GFX11-NEXT:    global_store_b64 v2, v[0:1], s[0:1]
+; GFX11-NEXT:    v_dual_mov_b32 v0, 0 :: v_dual_mov_b32 v1, 0
+; GFX11-NEXT:    v_mov_b32_e32 v2, s2
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
 ; GFX1250-LABEL: insert_extract_and_inreg_v4i32:
-; GFX1250:       ; %bb.0: ; %entry
+; GFX1250:       ; %bb.0:
 ; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX1250-NEXT:    s_wait_kmcnt 0x0
 ; GFX1250-NEXT:    s_and_b32 s2, s2, s16
@@ -16,7 +63,6 @@ define <4 x i32> @insert_extract_and_inreg_v4i32(ptr addrspace(1) inreg %out_sub
 ; GFX1250-NEXT:    v_dual_mov_b32 v0, 0 :: v_dual_mov_b32 v1, 0
 ; GFX1250-NEXT:    v_mov_b32_e32 v2, s2
 ; GFX1250-NEXT:    s_set_pc_i64 s[30:31]
-entry:
   %v0 = call <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32> zeroinitializer, <2 x i32> %a, i64 2)
   %v1 = call <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32> zeroinitializer, <2 x i32> %b, i64 2)
   %r = and <4 x i32> %v0, %v1
@@ -24,6 +70,3 @@ entry:
   store <2 x i32> %sub, ptr addrspace(1) %out_sub, align 8
   ret <4 x i32> %r
 }
-
-declare <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32>, <2 x i32>, i64 immarg)
-declare <2 x i32> @llvm.vector.extract.v2i32.v4i32(<4 x i32>, i64 immarg)



More information about the llvm-commits mailing list