https://github.com/MacDue created 
https://github.com/llvm/llvm-project/pull/181162

If we have a lowering for `VECREDUCE_MIN/MAX` this is generally more efficient 
than the scalar expansion.

>From 9b8aebe720e10ee2a2575202f2266553fed1c49f Mon Sep 17 00:00:00 2001
From: Benjamin Maxwell <[email protected]>
Date: Thu, 12 Feb 2026 14:39:05 +0000
Subject: [PATCH] [AArch64] Fold MIN/MAX(Vec[0], Vec[1]) to
 VECREDUCE_MIN/MAX(Vec)

If we have a lowering for `VECREDUCE_MIN/MAX` this is generally more
efficient than the scalar expansion.
---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 58 +++++++++--
 llvm/test/CodeGen/AArch64/v2i64-min-max.ll    | 99 +++++++++++++++++++
 2 files changed, 147 insertions(+), 10 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/v2i64-min-max.ll

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp 
b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index e55a75127235c..5621d43201bb1 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1160,7 +1160,7 @@ AArch64TargetLowering::AArch64TargetLowering(const 
TargetMachine &TM,
                        ISD::SIGN_EXTEND_INREG, ISD::CONCAT_VECTORS,
                        ISD::EXTRACT_SUBVECTOR, ISD::INSERT_SUBVECTOR,
                        ISD::STORE, ISD::BUILD_VECTOR});
-  setTargetDAGCombine(ISD::SMIN);
+  setTargetDAGCombine({ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX});
   setTargetDAGCombine(ISD::TRUNCATE);
   setTargetDAGCombine(ISD::LOAD);
 
@@ -22639,14 +22639,6 @@ static SDValue trySQDMULHCombine(SDNode *N, 
SelectionDAG &DAG) {
   return DAG.getNode(ISD::SIGN_EXTEND, DL, DestVT, SQDMULH);
 }
 
-static SDValue performSMINCombine(SDNode *N, SelectionDAG &DAG) {
-  if (SDValue V = trySQDMULHCombine(N, DAG)) {
-    return V;
-  }
-
-  return SDValue();
-}
-
 static SDValue performTruncateCombine(SDNode *N, SelectionDAG &DAG,
                                       TargetLowering::DAGCombinerInfo &DCI) {
   SDLoc DL(N);
@@ -28825,6 +28817,49 @@ static SDValue performCTPOPCombine(SDNode *N,
   return DAG.getNegative(NegPopCount, DL, VT);
 }
 
+static unsigned getReductionForOpcode(unsigned Op) {
+  switch (Op) {
+  case ISD::SMIN:
+    return ISD::VECREDUCE_SMIN;
+  case ISD::SMAX:
+    return ISD::VECREDUCE_SMAX;
+  case ISD::UMIN:
+    return ISD::VECREDUCE_UMIN;
+  case ISD::UMAX:
+    return ISD::VECREDUCE_UMAX;
+  default:
+    llvm_unreachable("unimplemented mapping");
+  }
+}
+
+static SDValue performMINMAXCombine(SDNode *N, SelectionDAG &DAG,
+                                    const AArch64Subtarget &Subtarget,
+                                    const AArch64TargetLowering &TLI) {
+  using namespace llvm::SDPatternMatch;
+  if (SDValue V = trySQDMULHCombine(N, DAG))
+    return V;
+
+  unsigned ReductionOpcode = getReductionForOpcode(N->getOpcode());
+  if (!TLI.isOperationLegalOrCustom(ReductionOpcode, MVT::v2i64))
+    return SDValue();
+
+  // Fold `min/max(vec[0], vec[1])` to `vecreduce_min/max(vec)` for v2i64.
+
+  APInt Idx;
+  SDValue Vec;
+  if (!sd_match(N->getOperand(0),
+                m_OneUse(m_ExtractElt(m_SpecificVT(MVT::v2i64, m_Value(Vec)),
+                                      m_ConstInt(Idx)))))
+    return SDValue();
+
+  if (!sd_match(
+          N->getOperand(1),
+          m_OneUse(m_ExtractElt(m_Specific(Vec), m_SpecificInt(1 - Idx)))))
+    return SDValue();
+
+  return DAG.getNode(ReductionOpcode, SDLoc(N), MVT::i64, Vec);
+}
+
 SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N,
                                                  DAGCombinerInfo &DCI) const {
   SelectionDAG &DAG = DCI.DAG;
@@ -28843,8 +28878,11 @@ SDValue 
AArch64TargetLowering::PerformDAGCombine(SDNode *N,
     return performAddSubCombine(N, DCI);
   case ISD::BUILD_VECTOR:
     return performBuildVectorCombine(N, DCI, DAG);
+  case ISD::UMAX:
+  case ISD::UMIN:
+  case ISD::SMAX:
   case ISD::SMIN:
-    return performSMINCombine(N, DAG);
+    return performMINMAXCombine(N, DAG, *Subtarget, *this);
   case ISD::TRUNCATE:
     return performTruncateCombine(N, DAG, DCI);
   case AArch64ISD::ANDS:
diff --git a/llvm/test/CodeGen/AArch64/v2i64-min-max.ll 
b/llvm/test/CodeGen/AArch64/v2i64-min-max.ll
new file mode 100644
index 0000000000000..43cd59413ba21
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/v2i64-min-max.ll
@@ -0,0 +1,99 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=aarch64 -mattr=+sve | FileCheck %s 
-check-prefix=CHECK-SVE
+; RUN: llc < %s -mtriple=aarch64 -global-isel | FileCheck %s 
-check-prefix=CHECK-NEON
+
+define i64 @smax(<2 x i64> %0) {
+; CHECK-SVE-LABEL: smax:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.d, vl2
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 def $z0
+; CHECK-SVE-NEXT:    smaxv d0, p0, z0.d
+; CHECK-SVE-NEXT:    fmov x0, d0
+; CHECK-SVE-NEXT:    ret
+;
+; CHECK-NEON-LABEL: smax:
+; CHECK-NEON:       // %bb.0:
+; CHECK-NEON-NEXT:    mov d1, v0.d[1]
+; CHECK-NEON-NEXT:    fmov x8, d0
+; CHECK-NEON-NEXT:    fmov x9, d1
+; CHECK-NEON-NEXT:    cmp x8, x9
+; CHECK-NEON-NEXT:    fcsel d0, d0, d1, gt
+; CHECK-NEON-NEXT:    fmov x0, d0
+; CHECK-NEON-NEXT:    ret
+  %2 = extractelement <2 x i64> %0, i64 0
+  %3 = extractelement <2 x i64> %0, i64 1
+  %4 = call i64 @llvm.smax.i64(i64 %2, i64 %3)
+  ret i64 %4
+}
+
+define i64 @umax(<2 x i64> %0) {
+; CHECK-SVE-LABEL: umax:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.d, vl2
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 def $z0
+; CHECK-SVE-NEXT:    umaxv d0, p0, z0.d
+; CHECK-SVE-NEXT:    fmov x0, d0
+; CHECK-SVE-NEXT:    ret
+;
+; CHECK-NEON-LABEL: umax:
+; CHECK-NEON:       // %bb.0:
+; CHECK-NEON-NEXT:    mov d1, v0.d[1]
+; CHECK-NEON-NEXT:    fmov x8, d0
+; CHECK-NEON-NEXT:    fmov x9, d1
+; CHECK-NEON-NEXT:    cmp x8, x9
+; CHECK-NEON-NEXT:    fcsel d0, d0, d1, hi
+; CHECK-NEON-NEXT:    fmov x0, d0
+; CHECK-NEON-NEXT:    ret
+  %2 = extractelement <2 x i64> %0, i64 0
+  %3 = extractelement <2 x i64> %0, i64 1
+  %4 = call i64 @llvm.umax.i64(i64 %2, i64 %3)
+  ret i64 %4
+}
+
+define i64 @smin(<2 x i64> %0) {
+; CHECK-SVE-LABEL: smin:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.d, vl2
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 def $z0
+; CHECK-SVE-NEXT:    sminv d0, p0, z0.d
+; CHECK-SVE-NEXT:    fmov x0, d0
+; CHECK-SVE-NEXT:    ret
+;
+; CHECK-NEON-LABEL: smin:
+; CHECK-NEON:       // %bb.0:
+; CHECK-NEON-NEXT:    mov d1, v0.d[1]
+; CHECK-NEON-NEXT:    fmov x8, d0
+; CHECK-NEON-NEXT:    fmov x9, d1
+; CHECK-NEON-NEXT:    cmp x8, x9
+; CHECK-NEON-NEXT:    fcsel d0, d0, d1, lt
+; CHECK-NEON-NEXT:    fmov x0, d0
+; CHECK-NEON-NEXT:    ret
+  %2 = extractelement <2 x i64> %0, i64 0
+  %3 = extractelement <2 x i64> %0, i64 1
+  %4 = call i64 @llvm.smin.i64(i64 %2, i64 %3)
+  ret i64 %4
+}
+
+define i64 @umin(<2 x i64> %0) {
+; CHECK-SVE-LABEL: umin:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.d, vl2
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 def $z0
+; CHECK-SVE-NEXT:    uminv d0, p0, z0.d
+; CHECK-SVE-NEXT:    fmov x0, d0
+; CHECK-SVE-NEXT:    ret
+;
+; CHECK-NEON-LABEL: umin:
+; CHECK-NEON:       // %bb.0:
+; CHECK-NEON-NEXT:    mov d1, v0.d[1]
+; CHECK-NEON-NEXT:    fmov x8, d0
+; CHECK-NEON-NEXT:    fmov x9, d1
+; CHECK-NEON-NEXT:    cmp x8, x9
+; CHECK-NEON-NEXT:    fcsel d0, d0, d1, lo
+; CHECK-NEON-NEXT:    fmov x0, d0
+; CHECK-NEON-NEXT:    ret
+  %2 = extractelement <2 x i64> %0, i64 0
+  %3 = extractelement <2 x i64> %0, i64 1
+  %4 = call i64 @llvm.umin.i64(i64 %2, i64 %3)
+  ret i64 %4
+}

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to