[llvm] [X86] Prefer SIMD min/max/abs for scalars when staying in XMM domain (PR #210654)

Shaurya Srivastava via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 11 01:33:20 PDT 2026


https://github.com/Shaurya2k06 updated https://github.com/llvm/llvm-project/pull/210654

>From e13b30488856e3d7548a4b0b913980e515c30968 Mon Sep 17 00:00:00 2001
From: shaurya2k06 <shaurya2k06 at gmail.com>
Date: Thu, 30 Jul 2026 18:03:45 +0530
Subject: [PATCH 1/2] [X86] Prefer SIMD min/max/abs for scalars when staying in
 XMM domain
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fold store(abs/min/max(load…)) of scalar i16/i32/i64 into a vector op via
SCALAR_TO_VECTOR, which ISel folds to movd/movq/vmovw + PABS/PMIN/PMAX.
This matches GCC for memory-bound sort2/abs and avoids introducing
GPR↔XMM domain crosses for values that already live in GPRs.

Implemented in combineStore (store-only), matching other load-op-store
folds like fabs/fneg. Only plain loads are accepted as sources. Paired
smin+smax of the same loads still convert via sibling-tolerant load-use
checks.

Supported types:
- i32: SSE4.1 (min/max) / SSSE3 (abs)
- i64: AVX512F+VLX (VLX avoids zmm widening + vzeroupper)
- i16: AVX512FP16 (VMOVW is the only mem->XMM fold for i16)

Bails under OptForSize, NoImplicitFloat, and useSoftFloat.

Fixes #210569

Assisted-by: Cursor
Signed-off-by: shaurya2k06 <shaurya2k06 at gmail.com>
---
 llvm/include/llvm/CodeGen/ISDOpcodes.h      |    6 +
 llvm/lib/Target/X86/X86ISelLowering.cpp     |   82 +
 llvm/test/CodeGen/X86/scalar-minmax-simd.ll | 1729 +++++++++++++++++++
 3 files changed, 1817 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/scalar-minmax-simd.ll

diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 23ef5d22963ae..f1c827baa4b9b 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1679,6 +1679,12 @@ inline bool isAbsOpcode(unsigned Opcode) {
   return Opcode == ISD::ABS || Opcode == ISD::ABS_MIN_POISON;
 }
 
+/// Whether this is an integer min/max opcode (ISD::(U|S)MIN or ISD::(U|S)MAX).
+inline bool isMinMaxOpcode(unsigned Opcode) {
+  return Opcode == ISD::SMIN || Opcode == ISD::SMAX || Opcode == ISD::UMIN ||
+         Opcode == ISD::UMAX;
+}
+
 /// Given a \p MinMaxOpc of ISD::(U|S)MIN or ISD::(U|S)MAX, returns
 /// ISD::(U|S)MAX and ISD::(U|S)MIN, respectively.
 LLVM_ABI NodeType getInverseMinMaxOpcode(unsigned MinMaxOpc);
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index d78c478ab672f..0e6a190d155d4 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -54617,6 +54617,83 @@ static SDValue narrowBitOpRMW(StoreSDNode *St, const SDLoc &DL,
   return NewStore;
 }
 
+/// Fold store(abs/min/max(load…)) of scalar i16/i32/i64 into SIMD
+/// PABS/PMIN/PMAX to keep memory-bound sort2/abs in the XMM domain.
+static SDValue combineScalarMinMaxAbsStore(StoreSDNode *St, const SDLoc &DL,
+                                           SelectionDAG &DAG,
+                                           const X86Subtarget &Subtarget) {
+  if (!ISD::isNormalStore(St))
+    return SDValue();
+
+  SDValue StoredVal = St->getValue();
+  unsigned Opc = StoredVal.getOpcode();
+  bool IsAbs = Opc == ISD::ABS;
+  if ((!IsAbs && !ISD::isMinMaxOpcode(Opc)) || !StoredVal.hasOneUse())
+    return SDValue();
+
+  EVT VT = StoredVal.getValueType();
+  const Function &F = DAG.getMachineFunction().getFunction();
+  if (F.hasFnAttribute(Attribute::NoImplicitFloat) ||
+      Subtarget.useSoftFloat() || F.hasOptSize())
+    return SDValue();
+
+  // i32: SSE4.1 (min/max) / SSSE3 (abs). i64: AVX512F+VLX (no VLX widens to
+  // zmm + vzeroupper). i16: AVX512FP16 (needs VMOVW for mem->XMM).
+  auto getVecVT = [&]() -> std::optional<MVT> {
+    if (VT == MVT::i32 && (IsAbs ? Subtarget.hasSSSE3() : Subtarget.hasSSE41()))
+      return MVT::v4i32;
+    if (VT == MVT::i64 && Subtarget.hasAVX512() && Subtarget.hasVLX())
+      return MVT::v2i64;
+    if (VT == MVT::i16 && Subtarget.hasFP16())
+      return MVT::v8i16;
+    return std::nullopt;
+  };
+  std::optional<MVT> VecVTOpt = getVecVT();
+  if (!VecVTOpt)
+    return SDValue();
+  MVT VecVT = *VecVTOpt;
+
+  SDValue Op0 = StoredVal.getOperand(0);
+  if (!ISD::isNormalLoad(Op0.getNode()))
+    return SDValue();
+
+  SDValue Vec;
+  if (IsAbs) {
+    if (!Op0.hasOneUse())
+      return SDValue();
+    Vec = DAG.getNode(ISD::ABS, DL, VecVT,
+                      DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, VecVT, Op0));
+  } else {
+    SDValue Op1 = StoredVal.getOperand(1);
+    if (!ISD::isNormalLoad(Op1.getNode()))
+      return SDValue();
+
+    // Loads may feed paired smin+smax (sort2); allow SCALAR_TO_VECTOR from a
+    // sibling already combined this turn so CSE can share them.
+    auto checkLoadUses = [](SDValue Ld) {
+      return all_of(Ld->uses(), [&](const SDUse &Use) {
+        if (Use.getResNo() != Ld.getResNo())
+          return true;
+        const SDNode *User = Use.getUser();
+        return User->getOpcode() == ISD::SCALAR_TO_VECTOR ||
+               (ISD::isMinMaxOpcode(User->getOpcode()) &&
+                !User->getValueType(0).isVector());
+      });
+    };
+    if (!checkLoadUses(Op0) || !checkLoadUses(Op1))
+      return SDValue();
+
+    Vec = DAG.getNode(Opc, DL, VecVT,
+                      DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, VecVT, Op0),
+                      DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, VecVT, Op1));
+  }
+
+  return DAG.getStore(St->getChain(), DL,
+                      DAG.getExtractVectorElt(DL, VT, Vec, 0), St->getBasePtr(),
+                      St->getPointerInfo(), St->getBaseAlign(),
+                      St->getMemOperand()->getFlags());
+}
+
 static SDValue combineStore(SDNode *N, SelectionDAG &DAG,
                             TargetLowering::DAGCombinerInfo &DCI,
                             const X86Subtarget &Subtarget) {
@@ -54742,6 +54819,11 @@ static SDValue combineStore(SDNode *N, SelectionDAG &DAG,
     }
   }
 
+  // Convert scalar abs/min/max of loads stored back to memory into SIMD
+  // PABS/PMIN/PMAX, keeping the values in the XMM domain.
+  if (SDValue R = combineScalarMinMaxAbsStore(St, dl, DAG, Subtarget))
+    return R;
+
   // If we are saving a 32-byte vector and 32-byte stores are slow, such as on
   // Sandy Bridge, perform two 16-byte stores.
   unsigned Fast;
diff --git a/llvm/test/CodeGen/X86/scalar-minmax-simd.ll b/llvm/test/CodeGen/X86/scalar-minmax-simd.ll
new file mode 100644
index 0000000000000..b574fd0859ac1
--- /dev/null
+++ b/llvm/test/CodeGen/X86/scalar-minmax-simd.ll
@@ -0,0 +1,1729 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+sse2 | FileCheck %s --check-prefixes=SSE2
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+sse4.1 | FileCheck %s --check-prefixes=SSE41
+; RUN: llc < %s -mtriple=i686-unknown-unknown -mattr=+sse4.1 | FileCheck %s --check-prefixes=X86-SSE41
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx | FileCheck %s --check-prefixes=AVX
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx512f | FileCheck %s --check-prefixes=AVX512F
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx512f,+avx512vl | FileCheck %s --check-prefixes=AVX512VL
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+avx512fp16 | FileCheck %s --check-prefixes=AVX512FP16
+
+; Memory-bound sort2 (#210569): prefer vpmin/vpmax over cmp+cmov.
+define void @sort2_i32(ptr %a) nounwind {
+; SSE2-LABEL: sort2_i32:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl 4(%rdi), %ecx
+; SSE2-NEXT:    cmpl %ecx, %eax
+; SSE2-NEXT:    movl %ecx, %edx
+; SSE2-NEXT:    cmovll %eax, %edx
+; SSE2-NEXT:    cmovgl %eax, %ecx
+; SSE2-NEXT:    movl %edx, (%rdi)
+; SSE2-NEXT:    movl %ecx, 4(%rdi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: sort2_i32:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; SSE41-NEXT:    movdqa %xmm1, %xmm2
+; SSE41-NEXT:    pminsd %xmm0, %xmm2
+; SSE41-NEXT:    movd %xmm2, (%rdi)
+; SSE41-NEXT:    pmaxsd %xmm0, %xmm1
+; SSE41-NEXT:    movd %xmm1, 4(%rdi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: sort2_i32:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    movdqa %xmm1, %xmm2
+; X86-SSE41-NEXT:    pminsd %xmm0, %xmm2
+; X86-SSE41-NEXT:    movd %xmm2, (%eax)
+; X86-SSE41-NEXT:    pmaxsd %xmm0, %xmm1
+; X86-SSE41-NEXT:    movd %xmm1, 4(%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: sort2_i32:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX-NEXT:    vpminsd %xmm0, %xmm1, %xmm2
+; AVX-NEXT:    vmovd %xmm2, (%rdi)
+; AVX-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vmovd %xmm0, 4(%rdi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: sort2_i32:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vpminsd %xmm0, %xmm1, %xmm2
+; AVX512F-NEXT:    vmovd %xmm2, (%rdi)
+; AVX512F-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX512F-NEXT:    vmovd %xmm0, 4(%rdi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: sort2_i32:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vpminsd %xmm0, %xmm1, %xmm2
+; AVX512VL-NEXT:    vmovd %xmm2, (%rdi)
+; AVX512VL-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX512VL-NEXT:    vmovd %xmm0, 4(%rdi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: sort2_i32:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vpminsd %xmm0, %xmm1, %xmm2
+; AVX512FP16-NEXT:    vmovd %xmm2, (%rdi)
+; AVX512FP16-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX512FP16-NEXT:    vmovd %xmm0, 4(%rdi)
+; AVX512FP16-NEXT:    retq
+  %p0 = getelementptr inbounds i32, ptr %a, i64 0
+  %p1 = getelementptr inbounds i32, ptr %a, i64 1
+  %x = load i32, ptr %p0, align 4
+  %y = load i32, ptr %p1, align 4
+  %lo = call i32 @llvm.smin.i32(i32 %x, i32 %y)
+  %hi = call i32 @llvm.smax.i32(i32 %x, i32 %y)
+  store i32 %lo, ptr %p0, align 4
+  store i32 %hi, ptr %p1, align 4
+  ret void
+}
+
+define void @store_smin_i32(ptr %p, ptr %q, ptr %r) nounwind {
+; SSE2-LABEL: store_smin_i32:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl (%rsi), %ecx
+; SSE2-NEXT:    cmpl %ecx, %eax
+; SSE2-NEXT:    cmovll %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rdx)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_smin_i32:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; SSE41-NEXT:    pminsd %xmm0, %xmm1
+; SSE41-NEXT:    movd %xmm1, (%rdx)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_smin_i32:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    pminsd %xmm0, %xmm1
+; X86-SSE41-NEXT:    movd %xmm1, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_smin_i32:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX-NEXT:    vpminsd %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vmovd %xmm0, (%rdx)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_smin_i32:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vpminsd %xmm0, %xmm1, %xmm0
+; AVX512F-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_smin_i32:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vpminsd %xmm0, %xmm1, %xmm0
+; AVX512VL-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_smin_i32:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vpminsd %xmm0, %xmm1, %xmm0
+; AVX512FP16-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %y = load i32, ptr %q, align 4
+  %m = call i32 @llvm.smin.i32(i32 %x, i32 %y)
+  store i32 %m, ptr %r, align 4
+  ret void
+}
+
+define void @store_smax_i32(ptr %p, ptr %q, ptr %r) nounwind {
+; SSE2-LABEL: store_smax_i32:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl (%rsi), %ecx
+; SSE2-NEXT:    cmpl %ecx, %eax
+; SSE2-NEXT:    cmovgl %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rdx)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_smax_i32:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; SSE41-NEXT:    pmaxsd %xmm0, %xmm1
+; SSE41-NEXT:    movd %xmm1, (%rdx)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_smax_i32:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    pmaxsd %xmm0, %xmm1
+; X86-SSE41-NEXT:    movd %xmm1, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_smax_i32:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vmovd %xmm0, (%rdx)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_smax_i32:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX512F-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_smax_i32:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX512VL-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_smax_i32:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vpmaxsd %xmm0, %xmm1, %xmm0
+; AVX512FP16-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %y = load i32, ptr %q, align 4
+  %m = call i32 @llvm.smax.i32(i32 %x, i32 %y)
+  store i32 %m, ptr %r, align 4
+  ret void
+}
+
+define void @store_umin_i32(ptr %p, ptr %q, ptr %r) nounwind {
+; SSE2-LABEL: store_umin_i32:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl (%rsi), %ecx
+; SSE2-NEXT:    cmpl %ecx, %eax
+; SSE2-NEXT:    cmovbl %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rdx)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_umin_i32:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; SSE41-NEXT:    pminud %xmm0, %xmm1
+; SSE41-NEXT:    movd %xmm1, (%rdx)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_umin_i32:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    pminud %xmm0, %xmm1
+; X86-SSE41-NEXT:    movd %xmm1, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_umin_i32:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX-NEXT:    vpminud %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vmovd %xmm0, (%rdx)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_umin_i32:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vpminud %xmm0, %xmm1, %xmm0
+; AVX512F-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_umin_i32:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vpminud %xmm0, %xmm1, %xmm0
+; AVX512VL-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_umin_i32:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vpminud %xmm0, %xmm1, %xmm0
+; AVX512FP16-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %y = load i32, ptr %q, align 4
+  %m = call i32 @llvm.umin.i32(i32 %x, i32 %y)
+  store i32 %m, ptr %r, align 4
+  ret void
+}
+
+define void @store_umax_i32(ptr %p, ptr %q, ptr %r) nounwind {
+; SSE2-LABEL: store_umax_i32:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl (%rsi), %ecx
+; SSE2-NEXT:    cmpl %ecx, %eax
+; SSE2-NEXT:    cmoval %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rdx)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_umax_i32:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; SSE41-NEXT:    pmaxud %xmm0, %xmm1
+; SSE41-NEXT:    movd %xmm1, (%rdx)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_umax_i32:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    pmaxud %xmm0, %xmm1
+; X86-SSE41-NEXT:    movd %xmm1, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_umax_i32:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX-NEXT:    vpmaxud %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vmovd %xmm0, (%rdx)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_umax_i32:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vpmaxud %xmm0, %xmm1, %xmm0
+; AVX512F-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_umax_i32:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vpmaxud %xmm0, %xmm1, %xmm0
+; AVX512VL-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_umax_i32:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vpmaxud %xmm0, %xmm1, %xmm0
+; AVX512FP16-NEXT:    vmovd %xmm0, (%rdx)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %y = load i32, ptr %q, align 4
+  %m = call i32 @llvm.umax.i32(i32 %x, i32 %y)
+  store i32 %m, ptr %r, align 4
+  ret void
+}
+
+; Negative: register operands / GPR result should stay on CMOV.
+define i32 @smin_gpr(i32 %x, i32 %y) nounwind {
+; SSE2-LABEL: smin_gpr:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl %esi, %eax
+; SSE2-NEXT:    cmpl %esi, %edi
+; SSE2-NEXT:    cmovll %edi, %eax
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: smin_gpr:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl %esi, %eax
+; SSE41-NEXT:    cmpl %esi, %edi
+; SSE41-NEXT:    cmovll %edi, %eax
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: smin_gpr:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    cmpl %eax, %ecx
+; X86-SSE41-NEXT:    cmovll %ecx, %eax
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: smin_gpr:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl %esi, %eax
+; AVX-NEXT:    cmpl %esi, %edi
+; AVX-NEXT:    cmovll %edi, %eax
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: smin_gpr:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl %esi, %eax
+; AVX512F-NEXT:    cmpl %esi, %edi
+; AVX512F-NEXT:    cmovll %edi, %eax
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: smin_gpr:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl %esi, %eax
+; AVX512VL-NEXT:    cmpl %esi, %edi
+; AVX512VL-NEXT:    cmovll %edi, %eax
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: smin_gpr:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl %esi, %eax
+; AVX512FP16-NEXT:    cmpl %esi, %edi
+; AVX512FP16-NEXT:    cmovll %edi, %eax
+; AVX512FP16-NEXT:    retq
+  %m = call i32 @llvm.smin.i32(i32 %x, i32 %y)
+  ret i32 %m
+}
+
+define i32 @smin_load_to_gpr(ptr %p, ptr %q) nounwind {
+; SSE2-LABEL: smin_load_to_gpr:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %ecx
+; SSE2-NEXT:    movl (%rsi), %eax
+; SSE2-NEXT:    cmpl %eax, %ecx
+; SSE2-NEXT:    cmovll %ecx, %eax
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: smin_load_to_gpr:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl (%rdi), %ecx
+; SSE41-NEXT:    movl (%rsi), %eax
+; SSE41-NEXT:    cmpl %eax, %ecx
+; SSE41-NEXT:    cmovll %ecx, %eax
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: smin_load_to_gpr:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl (%ecx), %ecx
+; X86-SSE41-NEXT:    movl (%eax), %eax
+; X86-SSE41-NEXT:    cmpl %eax, %ecx
+; X86-SSE41-NEXT:    cmovll %ecx, %eax
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: smin_load_to_gpr:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl (%rdi), %ecx
+; AVX-NEXT:    movl (%rsi), %eax
+; AVX-NEXT:    cmpl %eax, %ecx
+; AVX-NEXT:    cmovll %ecx, %eax
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: smin_load_to_gpr:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl (%rdi), %ecx
+; AVX512F-NEXT:    movl (%rsi), %eax
+; AVX512F-NEXT:    cmpl %eax, %ecx
+; AVX512F-NEXT:    cmovll %ecx, %eax
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: smin_load_to_gpr:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl (%rdi), %ecx
+; AVX512VL-NEXT:    movl (%rsi), %eax
+; AVX512VL-NEXT:    cmpl %eax, %ecx
+; AVX512VL-NEXT:    cmovll %ecx, %eax
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: smin_load_to_gpr:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl (%rdi), %ecx
+; AVX512FP16-NEXT:    movl (%rsi), %eax
+; AVX512FP16-NEXT:    cmpl %eax, %ecx
+; AVX512FP16-NEXT:    cmovll %ecx, %eax
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %y = load i32, ptr %q, align 4
+  %m = call i32 @llvm.smin.i32(i32 %x, i32 %y)
+  ret i32 %m
+}
+
+; Negative: noimplicitfloat must not introduce XMM ops.
+define void @sort2_i32_noimplicitfloat(ptr %a) nounwind noimplicitfloat {
+; SSE2-LABEL: sort2_i32_noimplicitfloat:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl 4(%rdi), %ecx
+; SSE2-NEXT:    cmpl %ecx, %eax
+; SSE2-NEXT:    movl %ecx, %edx
+; SSE2-NEXT:    cmovll %eax, %edx
+; SSE2-NEXT:    cmovgl %eax, %ecx
+; SSE2-NEXT:    movl %edx, (%rdi)
+; SSE2-NEXT:    movl %ecx, 4(%rdi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: sort2_i32_noimplicitfloat:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl (%rdi), %eax
+; SSE41-NEXT:    movl 4(%rdi), %ecx
+; SSE41-NEXT:    cmpl %ecx, %eax
+; SSE41-NEXT:    movl %ecx, %edx
+; SSE41-NEXT:    cmovll %eax, %edx
+; SSE41-NEXT:    cmovgl %eax, %ecx
+; SSE41-NEXT:    movl %edx, (%rdi)
+; SSE41-NEXT:    movl %ecx, 4(%rdi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: sort2_i32_noimplicitfloat:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl (%eax), %ecx
+; X86-SSE41-NEXT:    movl 4(%eax), %edx
+; X86-SSE41-NEXT:    cmpl %edx, %ecx
+; X86-SSE41-NEXT:    movl %edx, %esi
+; X86-SSE41-NEXT:    cmovll %ecx, %esi
+; X86-SSE41-NEXT:    cmovgl %ecx, %edx
+; X86-SSE41-NEXT:    movl %esi, (%eax)
+; X86-SSE41-NEXT:    movl %edx, 4(%eax)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: sort2_i32_noimplicitfloat:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl (%rdi), %eax
+; AVX-NEXT:    movl 4(%rdi), %ecx
+; AVX-NEXT:    cmpl %ecx, %eax
+; AVX-NEXT:    movl %ecx, %edx
+; AVX-NEXT:    cmovll %eax, %edx
+; AVX-NEXT:    cmovgl %eax, %ecx
+; AVX-NEXT:    movl %edx, (%rdi)
+; AVX-NEXT:    movl %ecx, 4(%rdi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: sort2_i32_noimplicitfloat:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl (%rdi), %eax
+; AVX512F-NEXT:    movl 4(%rdi), %ecx
+; AVX512F-NEXT:    cmpl %ecx, %eax
+; AVX512F-NEXT:    movl %ecx, %edx
+; AVX512F-NEXT:    cmovll %eax, %edx
+; AVX512F-NEXT:    cmovgl %eax, %ecx
+; AVX512F-NEXT:    movl %edx, (%rdi)
+; AVX512F-NEXT:    movl %ecx, 4(%rdi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: sort2_i32_noimplicitfloat:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl (%rdi), %eax
+; AVX512VL-NEXT:    movl 4(%rdi), %ecx
+; AVX512VL-NEXT:    cmpl %ecx, %eax
+; AVX512VL-NEXT:    movl %ecx, %edx
+; AVX512VL-NEXT:    cmovll %eax, %edx
+; AVX512VL-NEXT:    cmovgl %eax, %ecx
+; AVX512VL-NEXT:    movl %edx, (%rdi)
+; AVX512VL-NEXT:    movl %ecx, 4(%rdi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: sort2_i32_noimplicitfloat:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl (%rdi), %eax
+; AVX512FP16-NEXT:    movl 4(%rdi), %ecx
+; AVX512FP16-NEXT:    cmpl %ecx, %eax
+; AVX512FP16-NEXT:    movl %ecx, %edx
+; AVX512FP16-NEXT:    cmovll %eax, %edx
+; AVX512FP16-NEXT:    cmovgl %eax, %ecx
+; AVX512FP16-NEXT:    movl %edx, (%rdi)
+; AVX512FP16-NEXT:    movl %ecx, 4(%rdi)
+; AVX512FP16-NEXT:    retq
+  %p0 = getelementptr inbounds i32, ptr %a, i64 0
+  %p1 = getelementptr inbounds i32, ptr %a, i64 1
+  %x = load i32, ptr %p0, align 4
+  %y = load i32, ptr %p1, align 4
+  %lo = call i32 @llvm.smin.i32(i32 %x, i32 %y)
+  %hi = call i32 @llvm.smax.i32(i32 %x, i32 %y)
+  store i32 %lo, ptr %p0, align 4
+  store i32 %hi, ptr %p1, align 4
+  ret void
+}
+
+; Negative: optsize prefers compact cmp+cmov over movd+pmin/pmax.
+define void @sort2_i32_optsize(ptr %a) nounwind optsize {
+; SSE2-LABEL: sort2_i32_optsize:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl 4(%rdi), %ecx
+; SSE2-NEXT:    cmpl %ecx, %eax
+; SSE2-NEXT:    movl %ecx, %edx
+; SSE2-NEXT:    cmovll %eax, %edx
+; SSE2-NEXT:    cmovgl %eax, %ecx
+; SSE2-NEXT:    movl %edx, (%rdi)
+; SSE2-NEXT:    movl %ecx, 4(%rdi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: sort2_i32_optsize:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl (%rdi), %eax
+; SSE41-NEXT:    movl 4(%rdi), %ecx
+; SSE41-NEXT:    cmpl %ecx, %eax
+; SSE41-NEXT:    movl %ecx, %edx
+; SSE41-NEXT:    cmovll %eax, %edx
+; SSE41-NEXT:    cmovgl %eax, %ecx
+; SSE41-NEXT:    movl %edx, (%rdi)
+; SSE41-NEXT:    movl %ecx, 4(%rdi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: sort2_i32_optsize:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl (%eax), %ecx
+; X86-SSE41-NEXT:    movl 4(%eax), %edx
+; X86-SSE41-NEXT:    cmpl %edx, %ecx
+; X86-SSE41-NEXT:    movl %edx, %esi
+; X86-SSE41-NEXT:    cmovll %ecx, %esi
+; X86-SSE41-NEXT:    cmovgl %ecx, %edx
+; X86-SSE41-NEXT:    movl %esi, (%eax)
+; X86-SSE41-NEXT:    movl %edx, 4(%eax)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: sort2_i32_optsize:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl (%rdi), %eax
+; AVX-NEXT:    movl 4(%rdi), %ecx
+; AVX-NEXT:    cmpl %ecx, %eax
+; AVX-NEXT:    movl %ecx, %edx
+; AVX-NEXT:    cmovll %eax, %edx
+; AVX-NEXT:    cmovgl %eax, %ecx
+; AVX-NEXT:    movl %edx, (%rdi)
+; AVX-NEXT:    movl %ecx, 4(%rdi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: sort2_i32_optsize:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl (%rdi), %eax
+; AVX512F-NEXT:    movl 4(%rdi), %ecx
+; AVX512F-NEXT:    cmpl %ecx, %eax
+; AVX512F-NEXT:    movl %ecx, %edx
+; AVX512F-NEXT:    cmovll %eax, %edx
+; AVX512F-NEXT:    cmovgl %eax, %ecx
+; AVX512F-NEXT:    movl %edx, (%rdi)
+; AVX512F-NEXT:    movl %ecx, 4(%rdi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: sort2_i32_optsize:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl (%rdi), %eax
+; AVX512VL-NEXT:    movl 4(%rdi), %ecx
+; AVX512VL-NEXT:    cmpl %ecx, %eax
+; AVX512VL-NEXT:    movl %ecx, %edx
+; AVX512VL-NEXT:    cmovll %eax, %edx
+; AVX512VL-NEXT:    cmovgl %eax, %ecx
+; AVX512VL-NEXT:    movl %edx, (%rdi)
+; AVX512VL-NEXT:    movl %ecx, 4(%rdi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: sort2_i32_optsize:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl (%rdi), %eax
+; AVX512FP16-NEXT:    movl 4(%rdi), %ecx
+; AVX512FP16-NEXT:    cmpl %ecx, %eax
+; AVX512FP16-NEXT:    movl %ecx, %edx
+; AVX512FP16-NEXT:    cmovll %eax, %edx
+; AVX512FP16-NEXT:    cmovgl %eax, %ecx
+; AVX512FP16-NEXT:    movl %edx, (%rdi)
+; AVX512FP16-NEXT:    movl %ecx, 4(%rdi)
+; AVX512FP16-NEXT:    retq
+  %p0 = getelementptr inbounds i32, ptr %a, i64 0
+  %p1 = getelementptr inbounds i32, ptr %a, i64 1
+  %x = load i32, ptr %p0, align 4
+  %y = load i32, ptr %p1, align 4
+  %lo = call i32 @llvm.smin.i32(i32 %x, i32 %y)
+  %hi = call i32 @llvm.smax.i32(i32 %x, i32 %y)
+  store i32 %lo, ptr %p0, align 4
+  store i32 %hi, ptr %p1, align 4
+  ret void
+}
+
+; Negative: constant operands are not XMM-native sources; stay on CMOV.
+define void @store_smin_load_const(ptr %p, ptr %r) nounwind {
+; SSE2-LABEL: store_smin_load_const:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    cmpl $5, %eax
+; SSE2-NEXT:    movl $5, %ecx
+; SSE2-NEXT:    cmovll %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_smin_load_const:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl (%rdi), %eax
+; SSE41-NEXT:    cmpl $5, %eax
+; SSE41-NEXT:    movl $5, %ecx
+; SSE41-NEXT:    cmovll %eax, %ecx
+; SSE41-NEXT:    movl %ecx, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_smin_load_const:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl (%ecx), %ecx
+; X86-SSE41-NEXT:    cmpl $5, %ecx
+; X86-SSE41-NEXT:    movl $5, %edx
+; X86-SSE41-NEXT:    cmovll %ecx, %edx
+; X86-SSE41-NEXT:    movl %edx, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_smin_load_const:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl (%rdi), %eax
+; AVX-NEXT:    cmpl $5, %eax
+; AVX-NEXT:    movl $5, %ecx
+; AVX-NEXT:    cmovll %eax, %ecx
+; AVX-NEXT:    movl %ecx, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_smin_load_const:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl (%rdi), %eax
+; AVX512F-NEXT:    cmpl $5, %eax
+; AVX512F-NEXT:    movl $5, %ecx
+; AVX512F-NEXT:    cmovll %eax, %ecx
+; AVX512F-NEXT:    movl %ecx, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_smin_load_const:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl (%rdi), %eax
+; AVX512VL-NEXT:    cmpl $5, %eax
+; AVX512VL-NEXT:    movl $5, %ecx
+; AVX512VL-NEXT:    cmovll %eax, %ecx
+; AVX512VL-NEXT:    movl %ecx, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_smin_load_const:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl (%rdi), %eax
+; AVX512FP16-NEXT:    cmpl $5, %eax
+; AVX512FP16-NEXT:    movl $5, %ecx
+; AVX512FP16-NEXT:    cmovll %eax, %ecx
+; AVX512FP16-NEXT:    movl %ecx, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %m = call i32 @llvm.smin.i32(i32 %x, i32 5)
+  store i32 %m, ptr %r, align 4
+  ret void
+}
+
+; Memory-bound abs (#210569): prefer pabsd over neg+cmov.
+define void @store_abs_i32(ptr %p, ptr %r) nounwind {
+; SSE2-LABEL: store_abs_i32:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl %eax, %ecx
+; SSE2-NEXT:    negl %ecx
+; SSE2-NEXT:    cmovsl %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i32:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; SSE41-NEXT:    pabsd %xmm0, %xmm0
+; SSE41-NEXT:    movd %xmm0, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i32:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; X86-SSE41-NEXT:    pabsd %xmm0, %xmm0
+; X86-SSE41-NEXT:    movd %xmm0, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i32:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX-NEXT:    vpabsd %xmm0, %xmm0
+; AVX-NEXT:    vmovd %xmm0, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i32:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512F-NEXT:    vpabsd %xmm0, %xmm0
+; AVX512F-NEXT:    vmovd %xmm0, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i32:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512VL-NEXT:    vpabsd %xmm0, %xmm0
+; AVX512VL-NEXT:    vmovd %xmm0, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i32:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovd {{.*#+}} xmm0 = mem[0],zero,zero,zero
+; AVX512FP16-NEXT:    vpabsd %xmm0, %xmm0
+; AVX512FP16-NEXT:    vmovd %xmm0, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %a = call i32 @llvm.abs.i32(i32 %x, i1 false)
+  store i32 %a, ptr %r, align 4
+  ret void
+}
+
+; Negative: register operand / GPR result should stay on neg+cmov.
+define i32 @abs_gpr(i32 %x) nounwind {
+; SSE2-LABEL: abs_gpr:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl %edi, %eax
+; SSE2-NEXT:    negl %eax
+; SSE2-NEXT:    cmovsl %edi, %eax
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: abs_gpr:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl %edi, %eax
+; SSE41-NEXT:    negl %eax
+; SSE41-NEXT:    cmovsl %edi, %eax
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: abs_gpr:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl %ecx, %eax
+; X86-SSE41-NEXT:    negl %eax
+; X86-SSE41-NEXT:    cmovsl %ecx, %eax
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: abs_gpr:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl %edi, %eax
+; AVX-NEXT:    negl %eax
+; AVX-NEXT:    cmovsl %edi, %eax
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: abs_gpr:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl %edi, %eax
+; AVX512F-NEXT:    negl %eax
+; AVX512F-NEXT:    cmovsl %edi, %eax
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: abs_gpr:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl %edi, %eax
+; AVX512VL-NEXT:    negl %eax
+; AVX512VL-NEXT:    cmovsl %edi, %eax
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: abs_gpr:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl %edi, %eax
+; AVX512FP16-NEXT:    negl %eax
+; AVX512FP16-NEXT:    cmovsl %edi, %eax
+; AVX512FP16-NEXT:    retq
+  %a = call i32 @llvm.abs.i32(i32 %x, i1 false)
+  ret i32 %a
+}
+
+define i32 @abs_load_to_gpr(ptr %p) nounwind {
+; SSE2-LABEL: abs_load_to_gpr:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %ecx
+; SSE2-NEXT:    movl %ecx, %eax
+; SSE2-NEXT:    negl %eax
+; SSE2-NEXT:    cmovsl %ecx, %eax
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: abs_load_to_gpr:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl (%rdi), %ecx
+; SSE41-NEXT:    movl %ecx, %eax
+; SSE41-NEXT:    negl %eax
+; SSE41-NEXT:    cmovsl %ecx, %eax
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: abs_load_to_gpr:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl (%eax), %ecx
+; X86-SSE41-NEXT:    movl %ecx, %eax
+; X86-SSE41-NEXT:    negl %eax
+; X86-SSE41-NEXT:    cmovsl %ecx, %eax
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: abs_load_to_gpr:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl (%rdi), %ecx
+; AVX-NEXT:    movl %ecx, %eax
+; AVX-NEXT:    negl %eax
+; AVX-NEXT:    cmovsl %ecx, %eax
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: abs_load_to_gpr:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl (%rdi), %ecx
+; AVX512F-NEXT:    movl %ecx, %eax
+; AVX512F-NEXT:    negl %eax
+; AVX512F-NEXT:    cmovsl %ecx, %eax
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: abs_load_to_gpr:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl (%rdi), %ecx
+; AVX512VL-NEXT:    movl %ecx, %eax
+; AVX512VL-NEXT:    negl %eax
+; AVX512VL-NEXT:    cmovsl %ecx, %eax
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: abs_load_to_gpr:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl (%rdi), %ecx
+; AVX512FP16-NEXT:    movl %ecx, %eax
+; AVX512FP16-NEXT:    negl %eax
+; AVX512FP16-NEXT:    cmovsl %ecx, %eax
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %a = call i32 @llvm.abs.i32(i32 %x, i1 false)
+  ret i32 %a
+}
+
+; Negative: noimplicitfloat must not introduce XMM ops for abs.
+define void @store_abs_i32_noimplicitfloat(ptr %p, ptr %r) nounwind noimplicitfloat {
+; SSE2-LABEL: store_abs_i32_noimplicitfloat:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl %eax, %ecx
+; SSE2-NEXT:    negl %ecx
+; SSE2-NEXT:    cmovsl %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i32_noimplicitfloat:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl (%rdi), %eax
+; SSE41-NEXT:    movl %eax, %ecx
+; SSE41-NEXT:    negl %ecx
+; SSE41-NEXT:    cmovsl %eax, %ecx
+; SSE41-NEXT:    movl %ecx, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i32_noimplicitfloat:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl (%ecx), %ecx
+; X86-SSE41-NEXT:    movl %ecx, %edx
+; X86-SSE41-NEXT:    negl %edx
+; X86-SSE41-NEXT:    cmovsl %ecx, %edx
+; X86-SSE41-NEXT:    movl %edx, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i32_noimplicitfloat:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl (%rdi), %eax
+; AVX-NEXT:    movl %eax, %ecx
+; AVX-NEXT:    negl %ecx
+; AVX-NEXT:    cmovsl %eax, %ecx
+; AVX-NEXT:    movl %ecx, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i32_noimplicitfloat:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl (%rdi), %eax
+; AVX512F-NEXT:    movl %eax, %ecx
+; AVX512F-NEXT:    negl %ecx
+; AVX512F-NEXT:    cmovsl %eax, %ecx
+; AVX512F-NEXT:    movl %ecx, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i32_noimplicitfloat:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl (%rdi), %eax
+; AVX512VL-NEXT:    movl %eax, %ecx
+; AVX512VL-NEXT:    negl %ecx
+; AVX512VL-NEXT:    cmovsl %eax, %ecx
+; AVX512VL-NEXT:    movl %ecx, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i32_noimplicitfloat:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl (%rdi), %eax
+; AVX512FP16-NEXT:    movl %eax, %ecx
+; AVX512FP16-NEXT:    negl %ecx
+; AVX512FP16-NEXT:    cmovsl %eax, %ecx
+; AVX512FP16-NEXT:    movl %ecx, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %a = call i32 @llvm.abs.i32(i32 %x, i1 false)
+  store i32 %a, ptr %r, align 4
+  ret void
+}
+
+; Negative: optsize prefers compact neg+cmov over movd+pabsd.
+define void @store_abs_i32_optsize(ptr %p, ptr %r) nounwind optsize {
+; SSE2-LABEL: store_abs_i32_optsize:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movl (%rdi), %eax
+; SSE2-NEXT:    movl %eax, %ecx
+; SSE2-NEXT:    negl %ecx
+; SSE2-NEXT:    cmovsl %eax, %ecx
+; SSE2-NEXT:    movl %ecx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i32_optsize:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movl (%rdi), %eax
+; SSE41-NEXT:    movl %eax, %ecx
+; SSE41-NEXT:    negl %ecx
+; SSE41-NEXT:    cmovsl %eax, %ecx
+; SSE41-NEXT:    movl %ecx, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i32_optsize:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl (%ecx), %ecx
+; X86-SSE41-NEXT:    movl %ecx, %edx
+; X86-SSE41-NEXT:    negl %edx
+; X86-SSE41-NEXT:    cmovsl %ecx, %edx
+; X86-SSE41-NEXT:    movl %edx, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i32_optsize:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movl (%rdi), %eax
+; AVX-NEXT:    movl %eax, %ecx
+; AVX-NEXT:    negl %ecx
+; AVX-NEXT:    cmovsl %eax, %ecx
+; AVX-NEXT:    movl %ecx, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i32_optsize:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movl (%rdi), %eax
+; AVX512F-NEXT:    movl %eax, %ecx
+; AVX512F-NEXT:    negl %ecx
+; AVX512F-NEXT:    cmovsl %eax, %ecx
+; AVX512F-NEXT:    movl %ecx, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i32_optsize:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movl (%rdi), %eax
+; AVX512VL-NEXT:    movl %eax, %ecx
+; AVX512VL-NEXT:    negl %ecx
+; AVX512VL-NEXT:    cmovsl %eax, %ecx
+; AVX512VL-NEXT:    movl %ecx, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i32_optsize:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movl (%rdi), %eax
+; AVX512FP16-NEXT:    movl %eax, %ecx
+; AVX512FP16-NEXT:    negl %ecx
+; AVX512FP16-NEXT:    cmovsl %eax, %ecx
+; AVX512FP16-NEXT:    movl %ecx, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i32, ptr %p, align 4
+  %a = call i32 @llvm.abs.i32(i32 %x, i1 false)
+  store i32 %a, ptr %r, align 4
+  ret void
+}
+
+; i64 needs AVX512F+VLX (movq + vpminsq/vpmaxsq/vpabsq).
+define void @sort2_i64(ptr %a) nounwind {
+; SSE2-LABEL: sort2_i64:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movq (%rdi), %rax
+; SSE2-NEXT:    movq 8(%rdi), %rcx
+; SSE2-NEXT:    cmpq %rcx, %rax
+; SSE2-NEXT:    movq %rcx, %rdx
+; SSE2-NEXT:    cmovlq %rax, %rdx
+; SSE2-NEXT:    cmovgq %rax, %rcx
+; SSE2-NEXT:    movq %rdx, (%rdi)
+; SSE2-NEXT:    movq %rcx, 8(%rdi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: sort2_i64:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movq (%rdi), %rax
+; SSE41-NEXT:    movq 8(%rdi), %rcx
+; SSE41-NEXT:    cmpq %rcx, %rax
+; SSE41-NEXT:    movq %rcx, %rdx
+; SSE41-NEXT:    cmovlq %rax, %rdx
+; SSE41-NEXT:    cmovgq %rax, %rcx
+; SSE41-NEXT:    movq %rdx, (%rdi)
+; SSE41-NEXT:    movq %rcx, 8(%rdi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: sort2_i64:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %ebp
+; X86-SSE41-NEXT:    pushl %ebx
+; X86-SSE41-NEXT:    pushl %edi
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl (%ecx), %edx
+; X86-SSE41-NEXT:    movl 4(%ecx), %edi
+; X86-SSE41-NEXT:    movl 12(%ecx), %eax
+; X86-SSE41-NEXT:    movl 8(%ecx), %esi
+; X86-SSE41-NEXT:    cmpl %esi, %edx
+; X86-SSE41-NEXT:    movl %edi, %ebx
+; X86-SSE41-NEXT:    sbbl %eax, %ebx
+; X86-SSE41-NEXT:    movl %eax, %ebx
+; X86-SSE41-NEXT:    cmovll %edi, %ebx
+; X86-SSE41-NEXT:    movl %esi, %ebp
+; X86-SSE41-NEXT:    cmovll %edx, %ebp
+; X86-SSE41-NEXT:    cmpl %edx, %esi
+; X86-SSE41-NEXT:    movl %eax, %ecx
+; X86-SSE41-NEXT:    sbbl %edi, %ecx
+; X86-SSE41-NEXT:    cmovll %edi, %eax
+; X86-SSE41-NEXT:    cmovll %edx, %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl %ebp, (%ecx)
+; X86-SSE41-NEXT:    movl %ebx, 4(%ecx)
+; X86-SSE41-NEXT:    movl %esi, 8(%ecx)
+; X86-SSE41-NEXT:    movl %eax, 12(%ecx)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    popl %edi
+; X86-SSE41-NEXT:    popl %ebx
+; X86-SSE41-NEXT:    popl %ebp
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: sort2_i64:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movq (%rdi), %rax
+; AVX-NEXT:    movq 8(%rdi), %rcx
+; AVX-NEXT:    cmpq %rcx, %rax
+; AVX-NEXT:    movq %rcx, %rdx
+; AVX-NEXT:    cmovlq %rax, %rdx
+; AVX-NEXT:    cmovgq %rax, %rcx
+; AVX-NEXT:    movq %rdx, (%rdi)
+; AVX-NEXT:    movq %rcx, 8(%rdi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: sort2_i64:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movq (%rdi), %rax
+; AVX512F-NEXT:    movq 8(%rdi), %rcx
+; AVX512F-NEXT:    cmpq %rcx, %rax
+; AVX512F-NEXT:    movq %rcx, %rdx
+; AVX512F-NEXT:    cmovlq %rax, %rdx
+; AVX512F-NEXT:    cmovgq %rax, %rcx
+; AVX512F-NEXT:    movq %rdx, (%rdi)
+; AVX512F-NEXT:    movq %rcx, 8(%rdi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: sort2_i64:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovq {{.*#+}} xmm0 = mem[0],zero
+; AVX512VL-NEXT:    vmovq {{.*#+}} xmm1 = mem[0],zero
+; AVX512VL-NEXT:    vpminsq %xmm0, %xmm1, %xmm2
+; AVX512VL-NEXT:    vpmaxsq %xmm0, %xmm1, %xmm0
+; AVX512VL-NEXT:    vpunpcklqdq {{.*#+}} xmm0 = xmm2[0],xmm0[0]
+; AVX512VL-NEXT:    vmovdqu %xmm0, (%rdi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: sort2_i64:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movq (%rdi), %rax
+; AVX512FP16-NEXT:    movq 8(%rdi), %rcx
+; AVX512FP16-NEXT:    cmpq %rcx, %rax
+; AVX512FP16-NEXT:    movq %rcx, %rdx
+; AVX512FP16-NEXT:    cmovlq %rax, %rdx
+; AVX512FP16-NEXT:    cmovgq %rax, %rcx
+; AVX512FP16-NEXT:    movq %rdx, (%rdi)
+; AVX512FP16-NEXT:    movq %rcx, 8(%rdi)
+; AVX512FP16-NEXT:    retq
+  %p0 = getelementptr inbounds i64, ptr %a, i64 0
+  %p1 = getelementptr inbounds i64, ptr %a, i64 1
+  %x = load i64, ptr %p0, align 8
+  %y = load i64, ptr %p1, align 8
+  %lo = call i64 @llvm.smin.i64(i64 %x, i64 %y)
+  %hi = call i64 @llvm.smax.i64(i64 %x, i64 %y)
+  store i64 %lo, ptr %p0, align 8
+  store i64 %hi, ptr %p1, align 8
+  ret void
+}
+
+define void @store_abs_i64(ptr %p, ptr %r) nounwind {
+; SSE2-LABEL: store_abs_i64:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movq (%rdi), %rax
+; SSE2-NEXT:    movq %rax, %rcx
+; SSE2-NEXT:    negq %rcx
+; SSE2-NEXT:    cmovsq %rax, %rcx
+; SSE2-NEXT:    movq %rcx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i64:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movq (%rdi), %rax
+; SSE41-NEXT:    movq %rax, %rcx
+; SSE41-NEXT:    negq %rcx
+; SSE41-NEXT:    cmovsq %rax, %rcx
+; SSE41-NEXT:    movq %rcx, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i64:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl 4(%ecx), %edx
+; X86-SSE41-NEXT:    movl %edx, %esi
+; X86-SSE41-NEXT:    sarl $31, %esi
+; X86-SSE41-NEXT:    xorl %esi, %edx
+; X86-SSE41-NEXT:    movl (%ecx), %ecx
+; X86-SSE41-NEXT:    xorl %esi, %ecx
+; X86-SSE41-NEXT:    subl %esi, %ecx
+; X86-SSE41-NEXT:    sbbl %esi, %edx
+; X86-SSE41-NEXT:    movl %ecx, (%eax)
+; X86-SSE41-NEXT:    movl %edx, 4(%eax)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i64:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movq (%rdi), %rax
+; AVX-NEXT:    movq %rax, %rcx
+; AVX-NEXT:    negq %rcx
+; AVX-NEXT:    cmovsq %rax, %rcx
+; AVX-NEXT:    movq %rcx, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i64:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movq (%rdi), %rax
+; AVX512F-NEXT:    movq %rax, %rcx
+; AVX512F-NEXT:    negq %rcx
+; AVX512F-NEXT:    cmovsq %rax, %rcx
+; AVX512F-NEXT:    movq %rcx, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i64:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    vmovq {{.*#+}} xmm0 = mem[0],zero
+; AVX512VL-NEXT:    vpabsq %xmm0, %xmm0
+; AVX512VL-NEXT:    vmovq %xmm0, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i64:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movq (%rdi), %rax
+; AVX512FP16-NEXT:    movq %rax, %rcx
+; AVX512FP16-NEXT:    negq %rcx
+; AVX512FP16-NEXT:    cmovsq %rax, %rcx
+; AVX512FP16-NEXT:    movq %rcx, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i64, ptr %p, align 8
+  %a = call i64 @llvm.abs.i64(i64 %x, i1 false)
+  store i64 %a, ptr %r, align 8
+  ret void
+}
+
+; Negative: register / GPR-result i64 stays on cmp+cmov.
+define i64 @smin_gpr_i64(i64 %x, i64 %y) nounwind {
+; SSE2-LABEL: smin_gpr_i64:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movq %rsi, %rax
+; SSE2-NEXT:    cmpq %rsi, %rdi
+; SSE2-NEXT:    cmovlq %rdi, %rax
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: smin_gpr_i64:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movq %rsi, %rax
+; SSE41-NEXT:    cmpq %rsi, %rdi
+; SSE41-NEXT:    cmovlq %rdi, %rax
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: smin_gpr_i64:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %edi
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %esi
+; X86-SSE41-NEXT:    cmpl %eax, %ecx
+; X86-SSE41-NEXT:    movl %esi, %edi
+; X86-SSE41-NEXT:    sbbl %edx, %edi
+; X86-SSE41-NEXT:    cmovll %ecx, %eax
+; X86-SSE41-NEXT:    cmovll %esi, %edx
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    popl %edi
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: smin_gpr_i64:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movq %rsi, %rax
+; AVX-NEXT:    cmpq %rsi, %rdi
+; AVX-NEXT:    cmovlq %rdi, %rax
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: smin_gpr_i64:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movq %rsi, %rax
+; AVX512F-NEXT:    cmpq %rsi, %rdi
+; AVX512F-NEXT:    cmovlq %rdi, %rax
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: smin_gpr_i64:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movq %rsi, %rax
+; AVX512VL-NEXT:    cmpq %rsi, %rdi
+; AVX512VL-NEXT:    cmovlq %rdi, %rax
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: smin_gpr_i64:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movq %rsi, %rax
+; AVX512FP16-NEXT:    cmpq %rsi, %rdi
+; AVX512FP16-NEXT:    cmovlq %rdi, %rax
+; AVX512FP16-NEXT:    retq
+  %m = call i64 @llvm.smin.i64(i64 %x, i64 %y)
+  ret i64 %m
+}
+
+define void @sort2_i64_optsize(ptr %a) nounwind optsize {
+; SSE2-LABEL: sort2_i64_optsize:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movq (%rdi), %rax
+; SSE2-NEXT:    movq 8(%rdi), %rcx
+; SSE2-NEXT:    cmpq %rcx, %rax
+; SSE2-NEXT:    movq %rcx, %rdx
+; SSE2-NEXT:    cmovlq %rax, %rdx
+; SSE2-NEXT:    cmovgq %rax, %rcx
+; SSE2-NEXT:    movq %rdx, (%rdi)
+; SSE2-NEXT:    movq %rcx, 8(%rdi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: sort2_i64_optsize:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movq (%rdi), %rax
+; SSE41-NEXT:    movq 8(%rdi), %rcx
+; SSE41-NEXT:    cmpq %rcx, %rax
+; SSE41-NEXT:    movq %rcx, %rdx
+; SSE41-NEXT:    cmovlq %rax, %rdx
+; SSE41-NEXT:    cmovgq %rax, %rcx
+; SSE41-NEXT:    movq %rdx, (%rdi)
+; SSE41-NEXT:    movq %rcx, 8(%rdi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: sort2_i64_optsize:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %ebp
+; X86-SSE41-NEXT:    pushl %ebx
+; X86-SSE41-NEXT:    pushl %edi
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl (%ecx), %edx
+; X86-SSE41-NEXT:    movl 4(%ecx), %edi
+; X86-SSE41-NEXT:    movl 12(%ecx), %eax
+; X86-SSE41-NEXT:    movl 8(%ecx), %esi
+; X86-SSE41-NEXT:    cmpl %esi, %edx
+; X86-SSE41-NEXT:    movl %edi, %ebx
+; X86-SSE41-NEXT:    sbbl %eax, %ebx
+; X86-SSE41-NEXT:    movl %eax, %ebx
+; X86-SSE41-NEXT:    cmovll %edi, %ebx
+; X86-SSE41-NEXT:    movl %esi, %ebp
+; X86-SSE41-NEXT:    cmovll %edx, %ebp
+; X86-SSE41-NEXT:    cmpl %edx, %esi
+; X86-SSE41-NEXT:    movl %eax, %ecx
+; X86-SSE41-NEXT:    sbbl %edi, %ecx
+; X86-SSE41-NEXT:    cmovll %edi, %eax
+; X86-SSE41-NEXT:    cmovll %edx, %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl %ebp, (%ecx)
+; X86-SSE41-NEXT:    movl %ebx, 4(%ecx)
+; X86-SSE41-NEXT:    movl %esi, 8(%ecx)
+; X86-SSE41-NEXT:    movl %eax, 12(%ecx)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    popl %edi
+; X86-SSE41-NEXT:    popl %ebx
+; X86-SSE41-NEXT:    popl %ebp
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: sort2_i64_optsize:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movq (%rdi), %rax
+; AVX-NEXT:    movq 8(%rdi), %rcx
+; AVX-NEXT:    cmpq %rcx, %rax
+; AVX-NEXT:    movq %rcx, %rdx
+; AVX-NEXT:    cmovlq %rax, %rdx
+; AVX-NEXT:    cmovgq %rax, %rcx
+; AVX-NEXT:    movq %rdx, (%rdi)
+; AVX-NEXT:    movq %rcx, 8(%rdi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: sort2_i64_optsize:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movq (%rdi), %rax
+; AVX512F-NEXT:    movq 8(%rdi), %rcx
+; AVX512F-NEXT:    cmpq %rcx, %rax
+; AVX512F-NEXT:    movq %rcx, %rdx
+; AVX512F-NEXT:    cmovlq %rax, %rdx
+; AVX512F-NEXT:    cmovgq %rax, %rcx
+; AVX512F-NEXT:    movq %rdx, (%rdi)
+; AVX512F-NEXT:    movq %rcx, 8(%rdi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: sort2_i64_optsize:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movq (%rdi), %rax
+; AVX512VL-NEXT:    movq 8(%rdi), %rcx
+; AVX512VL-NEXT:    cmpq %rcx, %rax
+; AVX512VL-NEXT:    movq %rcx, %rdx
+; AVX512VL-NEXT:    cmovlq %rax, %rdx
+; AVX512VL-NEXT:    cmovgq %rax, %rcx
+; AVX512VL-NEXT:    movq %rdx, (%rdi)
+; AVX512VL-NEXT:    movq %rcx, 8(%rdi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: sort2_i64_optsize:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movq (%rdi), %rax
+; AVX512FP16-NEXT:    movq 8(%rdi), %rcx
+; AVX512FP16-NEXT:    cmpq %rcx, %rax
+; AVX512FP16-NEXT:    movq %rcx, %rdx
+; AVX512FP16-NEXT:    cmovlq %rax, %rdx
+; AVX512FP16-NEXT:    cmovgq %rax, %rcx
+; AVX512FP16-NEXT:    movq %rdx, (%rdi)
+; AVX512FP16-NEXT:    movq %rcx, 8(%rdi)
+; AVX512FP16-NEXT:    retq
+  %p0 = getelementptr inbounds i64, ptr %a, i64 0
+  %p1 = getelementptr inbounds i64, ptr %a, i64 1
+  %x = load i64, ptr %p0, align 8
+  %y = load i64, ptr %p1, align 8
+  %lo = call i64 @llvm.smin.i64(i64 %x, i64 %y)
+  %hi = call i64 @llvm.smax.i64(i64 %x, i64 %y)
+  store i64 %lo, ptr %p0, align 8
+  store i64 %hi, ptr %p1, align 8
+  ret void
+}
+
+define void @sort2_i64_noimplicitfloat(ptr %a) nounwind noimplicitfloat {
+; SSE2-LABEL: sort2_i64_noimplicitfloat:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movq (%rdi), %rax
+; SSE2-NEXT:    movq 8(%rdi), %rcx
+; SSE2-NEXT:    cmpq %rcx, %rax
+; SSE2-NEXT:    movq %rcx, %rdx
+; SSE2-NEXT:    cmovlq %rax, %rdx
+; SSE2-NEXT:    cmovgq %rax, %rcx
+; SSE2-NEXT:    movq %rdx, (%rdi)
+; SSE2-NEXT:    movq %rcx, 8(%rdi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: sort2_i64_noimplicitfloat:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movq (%rdi), %rax
+; SSE41-NEXT:    movq 8(%rdi), %rcx
+; SSE41-NEXT:    cmpq %rcx, %rax
+; SSE41-NEXT:    movq %rcx, %rdx
+; SSE41-NEXT:    cmovlq %rax, %rdx
+; SSE41-NEXT:    cmovgq %rax, %rcx
+; SSE41-NEXT:    movq %rdx, (%rdi)
+; SSE41-NEXT:    movq %rcx, 8(%rdi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: sort2_i64_noimplicitfloat:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %ebp
+; X86-SSE41-NEXT:    pushl %ebx
+; X86-SSE41-NEXT:    pushl %edi
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl (%ecx), %edx
+; X86-SSE41-NEXT:    movl 4(%ecx), %edi
+; X86-SSE41-NEXT:    movl 12(%ecx), %eax
+; X86-SSE41-NEXT:    movl 8(%ecx), %esi
+; X86-SSE41-NEXT:    cmpl %esi, %edx
+; X86-SSE41-NEXT:    movl %edi, %ebx
+; X86-SSE41-NEXT:    sbbl %eax, %ebx
+; X86-SSE41-NEXT:    movl %eax, %ebx
+; X86-SSE41-NEXT:    cmovll %edi, %ebx
+; X86-SSE41-NEXT:    movl %esi, %ebp
+; X86-SSE41-NEXT:    cmovll %edx, %ebp
+; X86-SSE41-NEXT:    cmpl %edx, %esi
+; X86-SSE41-NEXT:    movl %eax, %ecx
+; X86-SSE41-NEXT:    sbbl %edi, %ecx
+; X86-SSE41-NEXT:    cmovll %edi, %eax
+; X86-SSE41-NEXT:    cmovll %edx, %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movl %ebp, (%ecx)
+; X86-SSE41-NEXT:    movl %ebx, 4(%ecx)
+; X86-SSE41-NEXT:    movl %esi, 8(%ecx)
+; X86-SSE41-NEXT:    movl %eax, 12(%ecx)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    popl %edi
+; X86-SSE41-NEXT:    popl %ebx
+; X86-SSE41-NEXT:    popl %ebp
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: sort2_i64_noimplicitfloat:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movq (%rdi), %rax
+; AVX-NEXT:    movq 8(%rdi), %rcx
+; AVX-NEXT:    cmpq %rcx, %rax
+; AVX-NEXT:    movq %rcx, %rdx
+; AVX-NEXT:    cmovlq %rax, %rdx
+; AVX-NEXT:    cmovgq %rax, %rcx
+; AVX-NEXT:    movq %rdx, (%rdi)
+; AVX-NEXT:    movq %rcx, 8(%rdi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: sort2_i64_noimplicitfloat:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movq (%rdi), %rax
+; AVX512F-NEXT:    movq 8(%rdi), %rcx
+; AVX512F-NEXT:    cmpq %rcx, %rax
+; AVX512F-NEXT:    movq %rcx, %rdx
+; AVX512F-NEXT:    cmovlq %rax, %rdx
+; AVX512F-NEXT:    cmovgq %rax, %rcx
+; AVX512F-NEXT:    movq %rdx, (%rdi)
+; AVX512F-NEXT:    movq %rcx, 8(%rdi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: sort2_i64_noimplicitfloat:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movq (%rdi), %rax
+; AVX512VL-NEXT:    movq 8(%rdi), %rcx
+; AVX512VL-NEXT:    cmpq %rcx, %rax
+; AVX512VL-NEXT:    movq %rcx, %rdx
+; AVX512VL-NEXT:    cmovlq %rax, %rdx
+; AVX512VL-NEXT:    cmovgq %rax, %rcx
+; AVX512VL-NEXT:    movq %rdx, (%rdi)
+; AVX512VL-NEXT:    movq %rcx, 8(%rdi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: sort2_i64_noimplicitfloat:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    movq (%rdi), %rax
+; AVX512FP16-NEXT:    movq 8(%rdi), %rcx
+; AVX512FP16-NEXT:    cmpq %rcx, %rax
+; AVX512FP16-NEXT:    movq %rcx, %rdx
+; AVX512FP16-NEXT:    cmovlq %rax, %rdx
+; AVX512FP16-NEXT:    cmovgq %rax, %rcx
+; AVX512FP16-NEXT:    movq %rdx, (%rdi)
+; AVX512FP16-NEXT:    movq %rcx, 8(%rdi)
+; AVX512FP16-NEXT:    retq
+  %p0 = getelementptr inbounds i64, ptr %a, i64 0
+  %p1 = getelementptr inbounds i64, ptr %a, i64 1
+  %x = load i64, ptr %p0, align 8
+  %y = load i64, ptr %p1, align 8
+  %lo = call i64 @llvm.smin.i64(i64 %x, i64 %y)
+  %hi = call i64 @llvm.smax.i64(i64 %x, i64 %y)
+  store i64 %lo, ptr %p0, align 8
+  store i64 %hi, ptr %p1, align 8
+  ret void
+}
+
+; i16 needs AVX512FP16 (vmovw + vpminsw/vpmaxsw/vpabsw).
+define void @sort2_i16(ptr %a) nounwind {
+; SSE2-LABEL: sort2_i16:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movzwl (%rdi), %eax
+; SSE2-NEXT:    movzwl 2(%rdi), %ecx
+; SSE2-NEXT:    cmpw %cx, %ax
+; SSE2-NEXT:    movl %ecx, %edx
+; SSE2-NEXT:    cmovll %eax, %edx
+; SSE2-NEXT:    cmovgl %eax, %ecx
+; SSE2-NEXT:    movw %dx, (%rdi)
+; SSE2-NEXT:    movw %cx, 2(%rdi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: sort2_i16:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movzwl (%rdi), %eax
+; SSE41-NEXT:    movzwl 2(%rdi), %ecx
+; SSE41-NEXT:    cmpw %cx, %ax
+; SSE41-NEXT:    movl %ecx, %edx
+; SSE41-NEXT:    cmovll %eax, %edx
+; SSE41-NEXT:    cmovgl %eax, %ecx
+; SSE41-NEXT:    movw %dx, (%rdi)
+; SSE41-NEXT:    movw %cx, 2(%rdi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: sort2_i16:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    pushl %esi
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movzwl (%eax), %ecx
+; X86-SSE41-NEXT:    movzwl 2(%eax), %edx
+; X86-SSE41-NEXT:    cmpw %dx, %cx
+; X86-SSE41-NEXT:    movl %edx, %esi
+; X86-SSE41-NEXT:    cmovll %ecx, %esi
+; X86-SSE41-NEXT:    cmovgl %ecx, %edx
+; X86-SSE41-NEXT:    movw %si, (%eax)
+; X86-SSE41-NEXT:    movw %dx, 2(%eax)
+; X86-SSE41-NEXT:    popl %esi
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: sort2_i16:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movzwl (%rdi), %eax
+; AVX-NEXT:    movzwl 2(%rdi), %ecx
+; AVX-NEXT:    cmpw %cx, %ax
+; AVX-NEXT:    movl %ecx, %edx
+; AVX-NEXT:    cmovll %eax, %edx
+; AVX-NEXT:    cmovgl %eax, %ecx
+; AVX-NEXT:    movw %dx, (%rdi)
+; AVX-NEXT:    movw %cx, 2(%rdi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: sort2_i16:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movzwl (%rdi), %eax
+; AVX512F-NEXT:    movzwl 2(%rdi), %ecx
+; AVX512F-NEXT:    cmpw %cx, %ax
+; AVX512F-NEXT:    movl %ecx, %edx
+; AVX512F-NEXT:    cmovll %eax, %edx
+; AVX512F-NEXT:    cmovgl %eax, %ecx
+; AVX512F-NEXT:    movw %dx, (%rdi)
+; AVX512F-NEXT:    movw %cx, 2(%rdi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: sort2_i16:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movzwl (%rdi), %eax
+; AVX512VL-NEXT:    movzwl 2(%rdi), %ecx
+; AVX512VL-NEXT:    cmpw %cx, %ax
+; AVX512VL-NEXT:    movl %ecx, %edx
+; AVX512VL-NEXT:    cmovll %eax, %edx
+; AVX512VL-NEXT:    cmovgl %eax, %ecx
+; AVX512VL-NEXT:    movw %dx, (%rdi)
+; AVX512VL-NEXT:    movw %cx, 2(%rdi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: sort2_i16:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovw 2(%rdi), %xmm0
+; AVX512FP16-NEXT:    vmovw (%rdi), %xmm1
+; AVX512FP16-NEXT:    vpminsw %xmm0, %xmm1, %xmm2
+; AVX512FP16-NEXT:    vpextrw $0, %xmm2, (%rdi)
+; AVX512FP16-NEXT:    vpmaxsw %xmm0, %xmm1, %xmm0
+; AVX512FP16-NEXT:    vpextrw $0, %xmm0, 2(%rdi)
+; AVX512FP16-NEXT:    retq
+  %p0 = getelementptr inbounds i16, ptr %a, i64 0
+  %p1 = getelementptr inbounds i16, ptr %a, i64 1
+  %x = load i16, ptr %p0, align 2
+  %y = load i16, ptr %p1, align 2
+  %lo = call i16 @llvm.smin.i16(i16 %x, i16 %y)
+  %hi = call i16 @llvm.smax.i16(i16 %x, i16 %y)
+  store i16 %lo, ptr %p0, align 2
+  store i16 %hi, ptr %p1, align 2
+  ret void
+}
+
+define void @store_abs_i16(ptr %p, ptr %r) nounwind {
+; SSE2-LABEL: store_abs_i16:
+; SSE2:       # %bb.0:
+; SSE2-NEXT:    movzwl (%rdi), %eax
+; SSE2-NEXT:    movl %eax, %ecx
+; SSE2-NEXT:    negw %cx
+; SSE2-NEXT:    cmovsw %ax, %cx
+; SSE2-NEXT:    movw %cx, (%rsi)
+; SSE2-NEXT:    retq
+;
+; SSE41-LABEL: store_abs_i16:
+; SSE41:       # %bb.0:
+; SSE41-NEXT:    movzwl (%rdi), %eax
+; SSE41-NEXT:    movl %eax, %ecx
+; SSE41-NEXT:    negw %cx
+; SSE41-NEXT:    cmovsw %ax, %cx
+; SSE41-NEXT:    movw %cx, (%rsi)
+; SSE41-NEXT:    retq
+;
+; X86-SSE41-LABEL: store_abs_i16:
+; X86-SSE41:       # %bb.0:
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE41-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-SSE41-NEXT:    movzwl (%ecx), %ecx
+; X86-SSE41-NEXT:    movl %ecx, %edx
+; X86-SSE41-NEXT:    negw %dx
+; X86-SSE41-NEXT:    cmovsw %cx, %dx
+; X86-SSE41-NEXT:    movw %dx, (%eax)
+; X86-SSE41-NEXT:    retl
+;
+; AVX-LABEL: store_abs_i16:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movzwl (%rdi), %eax
+; AVX-NEXT:    movl %eax, %ecx
+; AVX-NEXT:    negw %cx
+; AVX-NEXT:    cmovsw %ax, %cx
+; AVX-NEXT:    movw %cx, (%rsi)
+; AVX-NEXT:    retq
+;
+; AVX512F-LABEL: store_abs_i16:
+; AVX512F:       # %bb.0:
+; AVX512F-NEXT:    movzwl (%rdi), %eax
+; AVX512F-NEXT:    movl %eax, %ecx
+; AVX512F-NEXT:    negw %cx
+; AVX512F-NEXT:    cmovsw %ax, %cx
+; AVX512F-NEXT:    movw %cx, (%rsi)
+; AVX512F-NEXT:    retq
+;
+; AVX512VL-LABEL: store_abs_i16:
+; AVX512VL:       # %bb.0:
+; AVX512VL-NEXT:    movzwl (%rdi), %eax
+; AVX512VL-NEXT:    movl %eax, %ecx
+; AVX512VL-NEXT:    negw %cx
+; AVX512VL-NEXT:    cmovsw %ax, %cx
+; AVX512VL-NEXT:    movw %cx, (%rsi)
+; AVX512VL-NEXT:    retq
+;
+; AVX512FP16-LABEL: store_abs_i16:
+; AVX512FP16:       # %bb.0:
+; AVX512FP16-NEXT:    vmovw (%rdi), %xmm0
+; AVX512FP16-NEXT:    vpabsw %xmm0, %xmm0
+; AVX512FP16-NEXT:    vpextrw $0, %xmm0, (%rsi)
+; AVX512FP16-NEXT:    retq
+  %x = load i16, ptr %p, align 2
+  %a = call i16 @llvm.abs.i16(i16 %x, i1 false)
+  store i16 %a, ptr %r, align 2
+  ret void
+}

>From 619626fd06a58fa0eaf999407c06d8764ba10148 Mon Sep 17 00:00:00 2001
From: shaurya2k06 <shaurya2k06 at gmail.com>
Date: Tue, 11 Aug 2026 14:03:04 +0530
Subject: [PATCH 2/2] [X86] Use isOperationLegal for scalar min/max/abs SIMD
 store fold

Gate the vector op via isOperationLegal, and separately require that
SCALAR_TO_VECTOR can fold the scalar load (i32/i64: SSE2; i16: FP16).
Keep the VLX guard for i64 to avoid zmm widening + vzeroupper.

Assisted-by: Cursor
Signed-off-by: shaurya2k06 <shaurya2k06 at gmail.com>
---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 32 ++++++++++++++++++-------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 0e6a190d155d4..7337309151504 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -54637,16 +54637,30 @@ static SDValue combineScalarMinMaxAbsStore(StoreSDNode *St, const SDLoc &DL,
       Subtarget.useSoftFloat() || F.hasOptSize())
     return SDValue();
 
-  // i32: SSE4.1 (min/max) / SSSE3 (abs). i64: AVX512F+VLX (no VLX widens to
-  // zmm + vzeroupper). i16: AVX512FP16 (needs VMOVW for mem->XMM).
+  const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+
+  // Pick the widened vector type; require a legal vector op and that
+  // SCALAR_TO_VECTOR can fold the scalar load into XMM (i32/i64: SSE2
+  // movd/movq; i16: FP16 vmovw). i64 also needs VLX to avoid zmm widen +
+  // vzeroupper.
   auto getVecVT = [&]() -> std::optional<MVT> {
-    if (VT == MVT::i32 && (IsAbs ? Subtarget.hasSSSE3() : Subtarget.hasSSE41()))
-      return MVT::v4i32;
-    if (VT == MVT::i64 && Subtarget.hasAVX512() && Subtarget.hasVLX())
-      return MVT::v2i64;
-    if (VT == MVT::i16 && Subtarget.hasFP16())
-      return MVT::v8i16;
-    return std::nullopt;
+    MVT VecVT;
+    if (VT == MVT::i32)
+      VecVT = MVT::v4i32;
+    else if (VT == MVT::i64)
+      VecVT = MVT::v2i64;
+    else if (VT == MVT::i16)
+      VecVT = MVT::v8i16;
+    else
+      return std::nullopt;
+
+    if (VT == MVT::i16 ? !Subtarget.hasFP16() : !Subtarget.hasSSE2())
+      return std::nullopt;
+    if (VT == MVT::i64 && !Subtarget.hasVLX())
+      return std::nullopt;
+    if (!TLI.isOperationLegal(Opc, VecVT))
+      return std::nullopt;
+    return VecVT;
   };
   std::optional<MVT> VecVTOpt = getVecVT();
   if (!VecVTOpt)



More information about the llvm-commits mailing list