[llvm] [X86] Narrow vXi32/vXi64 usubsat to vpsubusb/vpsubusw when LHS is known to fit in fewer bits (PR #206592)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 30 09:28:17 PDT 2026
https://github.com/sipher-01 updated https://github.com/llvm/llvm-project/pull/206592
>From 8786ad322bd939aa7696863428293a0abfad51ce Mon Sep 17 00:00:00 2001
From: Sipher <sourav2003singhkatoch at gmail.com>
Date: Tue, 30 Jun 2026 03:04:39 +0530
Subject: [PATCH] [X86] Narrow vXi32/vXi64 USUBSAT to vXi8/vXi16 when LHS fits
in fewer bits
When countMaxActiveBits(LHS) <= 8 or <= 16, reinterpret a wide vector
USUBSAT as vpsubusb/vpsubusw by OR-ing the upper bits of the RHS with 1s
so those byte/word lanes saturate to zero harmlessly.
This turns llvm.usub.sat.v8i64 emulation (vpmaxuq + vpsubq, 2 insns)
into a single vpsubusb when the LHS is known to fit in 8 bits.
Fixes https://github.com/llvm/llvm-project/issues/195462
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 36 +++
llvm/test/CodeGen/X86/psubus.ll | 112 ++++----
llvm/test/CodeGen/X86/usubsat-narrow.ll | 245 ++++++++++++++++++
3 files changed, 335 insertions(+), 58 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/usubsat-narrow.ll
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index c590f3c189ece..8ea760df382d3 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -4709,6 +4709,42 @@ SDValue DAGCombiner::visitSUBSAT(SDNode *N) {
if (DAG.willNotOverflowSub(IsSigned, N0, N1))
return DAG.getNode(ISD::SUB, DL, VT, N0, N1);
+ // Narrow a vXiN USUBSAT to a smaller type when the LHS is known to fit in
+ // fewer bits. If countMaxActiveBits(N0) <= NarrowBits, OR the upper bits of
+ // N1 with 1s so the narrow sat-sub saturates to zero in those positions.
+ // This allows targets with native narrow USUBSAT (e.g. vpsubusb/vpsubusw)
+ // to avoid emulation with vpmaxu + vsub.
+ if (!IsSigned && VT.isVector() && VT.isSimple()) {
+ unsigned ScalarBits = VT.getScalarSizeInBits();
+ if (ScalarBits > 8) {
+ if (TLI.isOperationLegal(ISD::USUBSAT, VT))
+ return SDValue();
+ KnownBits Known = DAG.computeKnownBits(N0);
+ unsigned ActiveBits = Known.countMaxActiveBits();
+ unsigned NarrowBits = 0;
+ if (ActiveBits <= 8 && ScalarBits > 16)
+ NarrowBits = 8;
+ else if (ActiveBits <= 16 && ScalarBits > 16)
+ NarrowBits = 16;
+ if (NarrowBits) {
+ unsigned Scale = ScalarBits / NarrowBits;
+ unsigned NumElts = VT.getVectorNumElements() * Scale;
+ MVT NarrowSVT = MVT::getIntegerVT(NarrowBits);
+ MVT NarrowVT = MVT::getVectorVT(NarrowSVT, NumElts);
+ if (TLI.isOperationLegalOrCustom(ISD::USUBSAT, NarrowVT)) {
+ APInt UpperMask = APInt::getBitsSetFrom(ScalarBits, NarrowBits);
+ SDValue Mask = DAG.getConstant(UpperMask, DL, VT);
+ SDValue N1WithUpperOnes = DAG.getNode(ISD::OR, DL, VT, N1, Mask);
+ SDValue NarrowN0 = DAG.getBitcast(NarrowVT, N0);
+ SDValue NarrowN1 = DAG.getBitcast(NarrowVT, N1WithUpperOnes);
+ SDValue NarrowSub =
+ DAG.getNode(ISD::USUBSAT, DL, NarrowVT, NarrowN0, NarrowN1);
+ return DAG.getBitcast(VT, NarrowSub);
+ }
+ }
+ }
+ }
+
return SDValue();
}
diff --git a/llvm/test/CodeGen/X86/psubus.ll b/llvm/test/CodeGen/X86/psubus.ll
index b93862be0a1b4..c0c71804ab425 100644
--- a/llvm/test/CodeGen/X86/psubus.ll
+++ b/llvm/test/CodeGen/X86/psubus.ll
@@ -2916,40 +2916,33 @@ define <8 x i32> @test34(<8 x i32> %a0, <8 x i64> %a1) {
; SSE2OR3-NEXT: pxor %xmm8, %xmm3
; SSE2OR3-NEXT: por %xmm2, %xmm3
; SSE2OR3-NEXT: shufps {{.*#+}} xmm3 = xmm3[0,2],xmm9[0,2]
-; SSE2OR3-NEXT: movdqa %xmm0, %xmm2
-; SSE2OR3-NEXT: psubd %xmm3, %xmm2
-; SSE2OR3-NEXT: pxor %xmm6, %xmm3
-; SSE2OR3-NEXT: por %xmm6, %xmm0
-; SSE2OR3-NEXT: pcmpgtd %xmm3, %xmm0
-; SSE2OR3-NEXT: pand %xmm2, %xmm0
-; SSE2OR3-NEXT: movdqa %xmm5, %xmm2
-; SSE2OR3-NEXT: pxor %xmm6, %xmm2
-; SSE2OR3-NEXT: pshufd {{.*#+}} xmm3 = xmm2[0,0,2,2]
-; SSE2OR3-NEXT: movdqa %xmm7, %xmm9
-; SSE2OR3-NEXT: pcmpgtd %xmm3, %xmm9
-; SSE2OR3-NEXT: pshufd {{.*#+}} xmm2 = xmm2[1,1,3,3]
-; SSE2OR3-NEXT: pcmpeqd %xmm6, %xmm2
-; SSE2OR3-NEXT: pand %xmm9, %xmm2
-; SSE2OR3-NEXT: pand %xmm2, %xmm5
-; SSE2OR3-NEXT: pxor %xmm8, %xmm2
-; SSE2OR3-NEXT: por %xmm5, %xmm2
-; SSE2OR3-NEXT: movdqa %xmm4, %xmm3
+; SSE2OR3-NEXT: movaps {{.*#+}} xmm2 = [4294967040,4294967040,4294967040,4294967040]
+; SSE2OR3-NEXT: orps %xmm2, %xmm3
+; SSE2OR3-NEXT: psubusb %xmm3, %xmm0
+; SSE2OR3-NEXT: movdqa %xmm5, %xmm3
; SSE2OR3-NEXT: pxor %xmm6, %xmm3
-; SSE2OR3-NEXT: pshufd {{.*#+}} xmm5 = xmm3[0,0,2,2]
-; SSE2OR3-NEXT: pcmpgtd %xmm5, %xmm7
+; SSE2OR3-NEXT: pshufd {{.*#+}} xmm9 = xmm3[0,0,2,2]
+; SSE2OR3-NEXT: movdqa %xmm7, %xmm10
+; SSE2OR3-NEXT: pcmpgtd %xmm9, %xmm10
; SSE2OR3-NEXT: pshufd {{.*#+}} xmm3 = xmm3[1,1,3,3]
; SSE2OR3-NEXT: pcmpeqd %xmm6, %xmm3
-; SSE2OR3-NEXT: pand %xmm7, %xmm3
-; SSE2OR3-NEXT: pxor %xmm3, %xmm8
-; SSE2OR3-NEXT: pand %xmm4, %xmm3
-; SSE2OR3-NEXT: por %xmm8, %xmm3
-; SSE2OR3-NEXT: shufps {{.*#+}} xmm3 = xmm3[0,2],xmm2[0,2]
-; SSE2OR3-NEXT: movdqa %xmm1, %xmm2
-; SSE2OR3-NEXT: psubd %xmm3, %xmm2
-; SSE2OR3-NEXT: pxor %xmm6, %xmm3
-; SSE2OR3-NEXT: por %xmm6, %xmm1
-; SSE2OR3-NEXT: pcmpgtd %xmm3, %xmm1
-; SSE2OR3-NEXT: pand %xmm2, %xmm1
+; SSE2OR3-NEXT: pand %xmm10, %xmm3
+; SSE2OR3-NEXT: pand %xmm3, %xmm5
+; SSE2OR3-NEXT: pxor %xmm8, %xmm3
+; SSE2OR3-NEXT: por %xmm5, %xmm3
+; SSE2OR3-NEXT: movdqa %xmm4, %xmm5
+; SSE2OR3-NEXT: pxor %xmm6, %xmm5
+; SSE2OR3-NEXT: pshufd {{.*#+}} xmm9 = xmm5[0,0,2,2]
+; SSE2OR3-NEXT: pcmpgtd %xmm9, %xmm7
+; SSE2OR3-NEXT: pshufd {{.*#+}} xmm5 = xmm5[1,1,3,3]
+; SSE2OR3-NEXT: pcmpeqd %xmm6, %xmm5
+; SSE2OR3-NEXT: pand %xmm7, %xmm5
+; SSE2OR3-NEXT: pxor %xmm5, %xmm8
+; SSE2OR3-NEXT: pand %xmm4, %xmm5
+; SSE2OR3-NEXT: por %xmm8, %xmm5
+; SSE2OR3-NEXT: shufps {{.*#+}} xmm5 = xmm5[0,2],xmm3[0,2]
+; SSE2OR3-NEXT: orps %xmm2, %xmm5
+; SSE2OR3-NEXT: psubusb %xmm5, %xmm1
; SSE2OR3-NEXT: retq
;
; SSE41-LABEL: test34:
@@ -2982,29 +2975,30 @@ define <8 x i32> @test34(<8 x i32> %a0, <8 x i64> %a1) {
; SSE41-NEXT: movapd %xmm8, %xmm3
; SSE41-NEXT: blendvpd %xmm0, %xmm2, %xmm3
; SSE41-NEXT: shufps {{.*#+}} xmm3 = xmm3[0,2],xmm10[0,2]
-; SSE41-NEXT: pmaxud %xmm3, %xmm6
-; SSE41-NEXT: psubd %xmm3, %xmm6
+; SSE41-NEXT: movaps {{.*#+}} xmm2 = [4294967040,4294967040,4294967040,4294967040]
+; SSE41-NEXT: orps %xmm2, %xmm3
+; SSE41-NEXT: psubusb %xmm3, %xmm6
; SSE41-NEXT: movdqa %xmm5, %xmm0
; SSE41-NEXT: pxor %xmm7, %xmm0
-; SSE41-NEXT: pshufd {{.*#+}} xmm2 = xmm0[0,0,2,2]
-; SSE41-NEXT: movdqa %xmm9, %xmm3
-; SSE41-NEXT: pcmpgtd %xmm2, %xmm3
+; SSE41-NEXT: pshufd {{.*#+}} xmm3 = xmm0[0,0,2,2]
+; SSE41-NEXT: movdqa %xmm9, %xmm10
+; SSE41-NEXT: pcmpgtd %xmm3, %xmm10
; SSE41-NEXT: pshufd {{.*#+}} xmm0 = xmm0[1,1,3,3]
; SSE41-NEXT: pcmpeqd %xmm7, %xmm0
-; SSE41-NEXT: pand %xmm3, %xmm0
-; SSE41-NEXT: movapd %xmm8, %xmm2
-; SSE41-NEXT: blendvpd %xmm0, %xmm5, %xmm2
+; SSE41-NEXT: pand %xmm10, %xmm0
+; SSE41-NEXT: movapd %xmm8, %xmm3
+; SSE41-NEXT: blendvpd %xmm0, %xmm5, %xmm3
; SSE41-NEXT: movdqa %xmm4, %xmm0
; SSE41-NEXT: pxor %xmm7, %xmm0
-; SSE41-NEXT: pshufd {{.*#+}} xmm3 = xmm0[0,0,2,2]
-; SSE41-NEXT: pcmpgtd %xmm3, %xmm9
+; SSE41-NEXT: pshufd {{.*#+}} xmm5 = xmm0[0,0,2,2]
+; SSE41-NEXT: pcmpgtd %xmm5, %xmm9
; SSE41-NEXT: pshufd {{.*#+}} xmm0 = xmm0[1,1,3,3]
; SSE41-NEXT: pcmpeqd %xmm7, %xmm0
; SSE41-NEXT: pand %xmm9, %xmm0
; SSE41-NEXT: blendvpd %xmm0, %xmm4, %xmm8
-; SSE41-NEXT: shufps {{.*#+}} xmm8 = xmm8[0,2],xmm2[0,2]
-; SSE41-NEXT: pmaxud %xmm8, %xmm1
-; SSE41-NEXT: psubd %xmm8, %xmm1
+; SSE41-NEXT: shufps {{.*#+}} xmm8 = xmm8[0,2],xmm3[0,2]
+; SSE41-NEXT: orps %xmm2, %xmm8
+; SSE41-NEXT: psubusb %xmm8, %xmm1
; SSE41-NEXT: movdqa %xmm6, %xmm0
; SSE41-NEXT: retq
;
@@ -3025,19 +3019,20 @@ define <8 x i32> @test34(<8 x i32> %a0, <8 x i64> %a1) {
; AVX1-NEXT: vpcmpgtq %xmm5, %xmm6, %xmm5
; AVX1-NEXT: vblendvpd %xmm5, %xmm2, %xmm7, %xmm2
; AVX1-NEXT: vshufps {{.*#+}} xmm2 = xmm2[0,2],xmm3[0,2]
-; AVX1-NEXT: vextractf128 $1, %ymm0, %xmm3
-; AVX1-NEXT: vpmaxud %xmm2, %xmm3, %xmm3
-; AVX1-NEXT: vpsubd %xmm2, %xmm3, %xmm2
-; AVX1-NEXT: vextractf128 $1, %ymm1, %xmm3
-; AVX1-NEXT: vpxor %xmm4, %xmm3, %xmm5
-; AVX1-NEXT: vpcmpgtq %xmm5, %xmm6, %xmm5
-; AVX1-NEXT: vblendvpd %xmm5, %xmm3, %xmm7, %xmm3
+; AVX1-NEXT: vbroadcastss {{.*#+}} xmm3 = [4294967040,4294967040,4294967040,4294967040]
+; AVX1-NEXT: vorps %xmm3, %xmm2, %xmm2
+; AVX1-NEXT: vextractf128 $1, %ymm0, %xmm5
+; AVX1-NEXT: vpsubusb %xmm2, %xmm5, %xmm2
+; AVX1-NEXT: vextractf128 $1, %ymm1, %xmm5
+; AVX1-NEXT: vpxor %xmm4, %xmm5, %xmm8
+; AVX1-NEXT: vpcmpgtq %xmm8, %xmm6, %xmm8
+; AVX1-NEXT: vblendvpd %xmm8, %xmm5, %xmm7, %xmm5
; AVX1-NEXT: vpxor %xmm4, %xmm1, %xmm4
; AVX1-NEXT: vpcmpgtq %xmm4, %xmm6, %xmm4
; AVX1-NEXT: vblendvpd %xmm4, %xmm1, %xmm7, %xmm1
-; AVX1-NEXT: vshufps {{.*#+}} xmm1 = xmm1[0,2],xmm3[0,2]
-; AVX1-NEXT: vpmaxud %xmm1, %xmm0, %xmm0
-; AVX1-NEXT: vpsubd %xmm1, %xmm0, %xmm0
+; AVX1-NEXT: vshufps {{.*#+}} xmm1 = xmm1[0,2],xmm5[0,2]
+; AVX1-NEXT: vorps %xmm3, %xmm1, %xmm1
+; AVX1-NEXT: vpsubusb %xmm1, %xmm0, %xmm0
; AVX1-NEXT: vinsertf128 $1, %xmm2, %ymm0, %ymm0
; AVX1-NEXT: retq
;
@@ -3056,16 +3051,17 @@ define <8 x i32> @test34(<8 x i32> %a0, <8 x i64> %a1) {
; AVX2-NEXT: vblendvpd %ymm3, %ymm1, %ymm6, %ymm1
; AVX2-NEXT: vshufps {{.*#+}} ymm1 = ymm1[0,2],ymm2[0,2],ymm1[4,6],ymm2[4,6]
; AVX2-NEXT: vpermpd {{.*#+}} ymm1 = ymm1[0,2,1,3]
-; AVX2-NEXT: vpmaxud %ymm1, %ymm0, %ymm0
-; AVX2-NEXT: vpsubd %ymm1, %ymm0, %ymm0
+; AVX2-NEXT: vbroadcastss {{.*#+}} ymm2 = [4294967040,4294967040,4294967040,4294967040,4294967040,4294967040,4294967040,4294967040]
+; AVX2-NEXT: vorps %ymm2, %ymm1, %ymm1
+; AVX2-NEXT: vpsubusb %ymm1, %ymm0, %ymm0
; AVX2-NEXT: retq
;
; AVX512-LABEL: test34:
; AVX512: # %bb.0:
; AVX512-NEXT: vpandd {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %ymm0, %ymm0
; AVX512-NEXT: vpmovusqd %zmm1, %ymm1
-; AVX512-NEXT: vpmaxud %ymm1, %ymm0, %ymm0
-; AVX512-NEXT: vpsubd %ymm1, %ymm0, %ymm0
+; AVX512-NEXT: vpord {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %ymm1, %ymm1
+; AVX512-NEXT: vpsubusb %ymm1, %ymm0, %ymm0
; AVX512-NEXT: retq
%mask = and <8 x i32> %a0, <i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1>
%zext = zext <8 x i32> %mask to <8 x i64>
diff --git a/llvm/test/CodeGen/X86/usubsat-narrow.ll b/llvm/test/CodeGen/X86/usubsat-narrow.ll
new file mode 100644
index 0000000000000..113904688721e
--- /dev/null
+++ b/llvm/test/CodeGen/X86/usubsat-narrow.ll
@@ -0,0 +1,245 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mcpu=x86-64 | FileCheck %s --check-prefix=SSE2
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mcpu=x86-64-v2 | FileCheck %s --check-prefix=SSE42
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mcpu=x86-64-v3| FileCheck %s --check-prefix=AVX2
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mcpu=x86-64-v4 | FileCheck %s --check-prefix=AVX512
+
+; v8i64 narrowed to v64i8 - LHS masked to 8 bits, should use vpsubusb
+define <8 x i64> @usubsat_v8i64_i8_narrow(<8 x i64> %x, <8 x i64> %y) nounwind {
+; SSE2-LABEL: usubsat_v8i64_i8_narrow:
+; SSE2: # %bb.0:
+; SSE2-NEXT: movdqa {{.*#+}} xmm8 = [255,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0]
+; SSE2-NEXT: pand %xmm8, %xmm3
+; SSE2-NEXT: pand %xmm8, %xmm2
+; SSE2-NEXT: pand %xmm8, %xmm1
+; SSE2-NEXT: pand %xmm8, %xmm0
+; SSE2-NEXT: movdqa {{.*#+}} xmm8 = [18446744073709551360,18446744073709551360]
+; SSE2-NEXT: por %xmm8, %xmm4
+; SSE2-NEXT: psubusb %xmm4, %xmm0
+; SSE2-NEXT: por %xmm8, %xmm5
+; SSE2-NEXT: psubusb %xmm5, %xmm1
+; SSE2-NEXT: por %xmm8, %xmm6
+; SSE2-NEXT: psubusb %xmm6, %xmm2
+; SSE2-NEXT: por %xmm8, %xmm7
+; SSE2-NEXT: psubusb %xmm7, %xmm3
+; SSE2-NEXT: retq
+;
+; SSE42-LABEL: usubsat_v8i64_i8_narrow:
+; SSE42: # %bb.0:
+; SSE42-NEXT: movdqa {{.*#+}} xmm8 = [255,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0]
+; SSE42-NEXT: pand %xmm8, %xmm3
+; SSE42-NEXT: pand %xmm8, %xmm2
+; SSE42-NEXT: pand %xmm8, %xmm1
+; SSE42-NEXT: pand %xmm8, %xmm0
+; SSE42-NEXT: movdqa {{.*#+}} xmm8 = [18446744073709551360,18446744073709551360]
+; SSE42-NEXT: por %xmm8, %xmm4
+; SSE42-NEXT: psubusb %xmm4, %xmm0
+; SSE42-NEXT: por %xmm8, %xmm5
+; SSE42-NEXT: psubusb %xmm5, %xmm1
+; SSE42-NEXT: por %xmm8, %xmm6
+; SSE42-NEXT: psubusb %xmm6, %xmm2
+; SSE42-NEXT: por %xmm8, %xmm7
+; SSE42-NEXT: psubusb %xmm7, %xmm3
+; SSE42-NEXT: retq
+;
+; AVX2-LABEL: usubsat_v8i64_i8_narrow:
+; AVX2: # %bb.0:
+; AVX2-NEXT: vpbroadcastq {{.*#+}} ymm4 = [255,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0]
+; AVX2-NEXT: vpand %ymm4, %ymm1, %ymm1
+; AVX2-NEXT: vpand %ymm4, %ymm0, %ymm0
+; AVX2-NEXT: vpbroadcastq {{.*#+}} ymm4 = [18446744073709551360,18446744073709551360,18446744073709551360,18446744073709551360]
+; AVX2-NEXT: vpor %ymm4, %ymm2, %ymm2
+; AVX2-NEXT: vpsubusb %ymm2, %ymm0, %ymm0
+; AVX2-NEXT: vpor %ymm4, %ymm3, %ymm2
+; AVX2-NEXT: vpsubusb %ymm2, %ymm1, %ymm1
+; AVX2-NEXT: retq
+;
+; AVX512-LABEL: usubsat_v8i64_i8_narrow:
+; AVX512: # %bb.0:
+; AVX512-NEXT: vpandq {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm0
+; AVX512-NEXT: vporq {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm1, %zmm1
+; AVX512-NEXT: vpsubusb %zmm1, %zmm0, %zmm0
+; AVX512-NEXT: retq
+ %masked = and <8 x i64> %x, <i64 255, i64 255, i64 255, i64 255, i64 255, i64 255, i64 255, i64 255>
+ %z = call <8 x i64> @llvm.usub.sat.v8i64(<8 x i64> %masked, <8 x i64> %y)
+ ret <8 x i64> %z
+}
+
+; v4i64 narrowed to v32i8 - LHS masked to 8 bits, should use vpsubusb
+define <4 x i64> @usubsat_v4i64_i8_narrow(<4 x i64> %x, <4 x i64> %y) nounwind {
+; SSE2-LABEL: usubsat_v4i64_i8_narrow:
+; SSE2: # %bb.0:
+; SSE2-NEXT: movdqa {{.*#+}} xmm4 = [255,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0]
+; SSE2-NEXT: pand %xmm4, %xmm1
+; SSE2-NEXT: pand %xmm4, %xmm0
+; SSE2-NEXT: movdqa {{.*#+}} xmm4 = [18446744073709551360,18446744073709551360]
+; SSE2-NEXT: por %xmm4, %xmm2
+; SSE2-NEXT: psubusb %xmm2, %xmm0
+; SSE2-NEXT: por %xmm4, %xmm3
+; SSE2-NEXT: psubusb %xmm3, %xmm1
+; SSE2-NEXT: retq
+;
+; SSE42-LABEL: usubsat_v4i64_i8_narrow:
+; SSE42: # %bb.0:
+; SSE42-NEXT: movdqa {{.*#+}} xmm4 = [255,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0]
+; SSE42-NEXT: pand %xmm4, %xmm1
+; SSE42-NEXT: pand %xmm4, %xmm0
+; SSE42-NEXT: movdqa {{.*#+}} xmm4 = [18446744073709551360,18446744073709551360]
+; SSE42-NEXT: por %xmm4, %xmm2
+; SSE42-NEXT: psubusb %xmm2, %xmm0
+; SSE42-NEXT: por %xmm4, %xmm3
+; SSE42-NEXT: psubusb %xmm3, %xmm1
+; SSE42-NEXT: retq
+;
+; AVX2-LABEL: usubsat_v4i64_i8_narrow:
+; AVX2: # %bb.0:
+; AVX2-NEXT: vpand {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm0
+; AVX2-NEXT: vpbroadcastq {{.*#+}} ymm2 = [18446744073709551360,18446744073709551360,18446744073709551360,18446744073709551360]
+; AVX2-NEXT: vpor %ymm2, %ymm1, %ymm1
+; AVX2-NEXT: vpsubusb %ymm1, %ymm0, %ymm0
+; AVX2-NEXT: retq
+;
+; AVX512-LABEL: usubsat_v4i64_i8_narrow:
+; AVX512: # %bb.0:
+; AVX512-NEXT: vpandq {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to4}, %ymm0, %ymm0
+; AVX512-NEXT: vporq {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to4}, %ymm1, %ymm1
+; AVX512-NEXT: vpsubusb %ymm1, %ymm0, %ymm0
+; AVX512-NEXT: retq
+ %masked = and <4 x i64> %x, <i64 255, i64 255, i64 255, i64 255>
+ %z = call <4 x i64> @llvm.usub.sat.v4i64(<4 x i64> %masked, <4 x i64> %y)
+ ret <4 x i64> %z
+}
+
+; v8i32 narrowed to v32i8 - LHS masked to 8 bits, should use vpsubusb
+define <8 x i32> @usubsat_v8i32_i8_narrow(<8 x i32> %x, <8 x i32> %y) nounwind {
+; SSE2-LABEL: usubsat_v8i32_i8_narrow:
+; SSE2: # %bb.0:
+; SSE2-NEXT: movdqa {{.*#+}} xmm4 = [255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0]
+; SSE2-NEXT: pand %xmm4, %xmm1
+; SSE2-NEXT: pand %xmm4, %xmm0
+; SSE2-NEXT: movdqa {{.*#+}} xmm4 = [4294967040,4294967040,4294967040,4294967040]
+; SSE2-NEXT: por %xmm4, %xmm2
+; SSE2-NEXT: psubusb %xmm2, %xmm0
+; SSE2-NEXT: por %xmm4, %xmm3
+; SSE2-NEXT: psubusb %xmm3, %xmm1
+; SSE2-NEXT: retq
+;
+; SSE42-LABEL: usubsat_v8i32_i8_narrow:
+; SSE42: # %bb.0:
+; SSE42-NEXT: movdqa {{.*#+}} xmm4 = [255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0]
+; SSE42-NEXT: pand %xmm4, %xmm1
+; SSE42-NEXT: pand %xmm4, %xmm0
+; SSE42-NEXT: movdqa {{.*#+}} xmm4 = [4294967040,4294967040,4294967040,4294967040]
+; SSE42-NEXT: por %xmm4, %xmm2
+; SSE42-NEXT: psubusb %xmm2, %xmm0
+; SSE42-NEXT: por %xmm4, %xmm3
+; SSE42-NEXT: psubusb %xmm3, %xmm1
+; SSE42-NEXT: retq
+;
+; AVX2-LABEL: usubsat_v8i32_i8_narrow:
+; AVX2: # %bb.0:
+; AVX2-NEXT: vpand {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm0
+; AVX2-NEXT: vpbroadcastd {{.*#+}} ymm2 = [4294967040,4294967040,4294967040,4294967040,4294967040,4294967040,4294967040,4294967040]
+; AVX2-NEXT: vpor %ymm2, %ymm1, %ymm1
+; AVX2-NEXT: vpsubusb %ymm1, %ymm0, %ymm0
+; AVX2-NEXT: retq
+;
+; AVX512-LABEL: usubsat_v8i32_i8_narrow:
+; AVX512: # %bb.0:
+; AVX512-NEXT: vpandd {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %ymm0, %ymm0
+; AVX512-NEXT: vpord {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %ymm1, %ymm1
+; AVX512-NEXT: vpsubusb %ymm1, %ymm0, %ymm0
+; AVX512-NEXT: retq
+ %masked = and <8 x i32> %x, <i32 255, i32 255, i32 255, i32 255, i32 255, i32 255, i32 255, i32 255>
+ %z = call <8 x i32> @llvm.usub.sat.v8i32(<8 x i32> %masked, <8 x i32> %y)
+ ret <8 x i32> %z
+}
+
+; v8i32 narrowed to v16i16 - LHS masked to 16 bits, should use vpsubusw
+define <8 x i32> @usubsat_v8i32_i16_narrow(<8 x i32> %x, <8 x i32> %y) nounwind {
+; SSE2-LABEL: usubsat_v8i32_i16_narrow:
+; SSE2: # %bb.0:
+; SSE2-NEXT: movdqa {{.*#+}} xmm4 = [65535,0,65535,0,65535,0,65535,0]
+; SSE2-NEXT: pand %xmm4, %xmm1
+; SSE2-NEXT: pand %xmm4, %xmm0
+; SSE2-NEXT: movdqa {{.*#+}} xmm4 = [4294901760,4294901760,4294901760,4294901760]
+; SSE2-NEXT: por %xmm4, %xmm2
+; SSE2-NEXT: psubusw %xmm2, %xmm0
+; SSE2-NEXT: por %xmm4, %xmm3
+; SSE2-NEXT: psubusw %xmm3, %xmm1
+; SSE2-NEXT: retq
+;
+; SSE42-LABEL: usubsat_v8i32_i16_narrow:
+; SSE42: # %bb.0:
+; SSE42-NEXT: pxor %xmm4, %xmm4
+; SSE42-NEXT: pblendw {{.*#+}} xmm1 = xmm1[0],xmm4[1],xmm1[2],xmm4[3],xmm1[4],xmm4[5],xmm1[6],xmm4[7]
+; SSE42-NEXT: pblendw {{.*#+}} xmm0 = xmm0[0],xmm4[1],xmm0[2],xmm4[3],xmm0[4],xmm4[5],xmm0[6],xmm4[7]
+; SSE42-NEXT: movdqa {{.*#+}} xmm4 = [4294901760,4294901760,4294901760,4294901760]
+; SSE42-NEXT: por %xmm4, %xmm2
+; SSE42-NEXT: psubusw %xmm2, %xmm0
+; SSE42-NEXT: por %xmm4, %xmm3
+; SSE42-NEXT: psubusw %xmm3, %xmm1
+; SSE42-NEXT: retq
+;
+; AVX2-LABEL: usubsat_v8i32_i16_narrow:
+; AVX2: # %bb.0:
+; AVX2-NEXT: vpxor %xmm2, %xmm2, %xmm2
+; AVX2-NEXT: vpblendw {{.*#+}} ymm0 = ymm0[0],ymm2[1],ymm0[2],ymm2[3],ymm0[4],ymm2[5],ymm0[6],ymm2[7],ymm0[8],ymm2[9],ymm0[10],ymm2[11],ymm0[12],ymm2[13],ymm0[14],ymm2[15]
+; AVX2-NEXT: vpbroadcastd {{.*#+}} ymm2 = [4294901760,4294901760,4294901760,4294901760,4294901760,4294901760,4294901760,4294901760]
+; AVX2-NEXT: vpor %ymm2, %ymm1, %ymm1
+; AVX2-NEXT: vpsubusw %ymm1, %ymm0, %ymm0
+; AVX2-NEXT: retq
+;
+; AVX512-LABEL: usubsat_v8i32_i16_narrow:
+; AVX512: # %bb.0:
+; AVX512-NEXT: vpxor %xmm2, %xmm2, %xmm2
+; AVX512-NEXT: vpblendw {{.*#+}} ymm0 = ymm0[0],ymm2[1],ymm0[2],ymm2[3],ymm0[4],ymm2[5],ymm0[6],ymm2[7],ymm0[8],ymm2[9],ymm0[10],ymm2[11],ymm0[12],ymm2[13],ymm0[14],ymm2[15]
+; AVX512-NEXT: vpord {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %ymm1, %ymm1
+; AVX512-NEXT: vpsubusw %ymm1, %ymm0, %ymm0
+; AVX512-NEXT: retq
+ %masked = and <8 x i32> %x, <i32 65535, i32 65535, i32 65535, i32 65535, i32 65535, i32 65535, i32 65535, i32 65535>
+ %z = call <8 x i32> @llvm.usub.sat.v8i32(<8 x i32> %masked, <8 x i32> %y)
+ ret <8 x i32> %z
+}
+
+; Negative test: LHS values may exceed 16 bits, should NOT narrow
+define <8 x i32> @usubsat_v8i32_no_narrow(<8 x i32> %x, <8 x i32> %y) nounwind {
+; SSE2-LABEL: usubsat_v8i32_no_narrow:
+; SSE2: # %bb.0:
+; SSE2-NEXT: movdqa {{.*#+}} xmm4 = [2147483648,2147483648,2147483648,2147483648]
+; SSE2-NEXT: movdqa %xmm0, %xmm5
+; SSE2-NEXT: psubd %xmm2, %xmm0
+; SSE2-NEXT: pxor %xmm4, %xmm2
+; SSE2-NEXT: pxor %xmm4, %xmm5
+; SSE2-NEXT: pcmpgtd %xmm2, %xmm5
+; SSE2-NEXT: pand %xmm5, %xmm0
+; SSE2-NEXT: movdqa %xmm3, %xmm2
+; SSE2-NEXT: pxor %xmm4, %xmm2
+; SSE2-NEXT: pxor %xmm1, %xmm4
+; SSE2-NEXT: pcmpgtd %xmm2, %xmm4
+; SSE2-NEXT: psubd %xmm3, %xmm1
+; SSE2-NEXT: pand %xmm4, %xmm1
+; SSE2-NEXT: retq
+;
+; SSE42-LABEL: usubsat_v8i32_no_narrow:
+; SSE42: # %bb.0:
+; SSE42-NEXT: pmaxud %xmm2, %xmm0
+; SSE42-NEXT: psubd %xmm2, %xmm0
+; SSE42-NEXT: pmaxud %xmm3, %xmm1
+; SSE42-NEXT: psubd %xmm3, %xmm1
+; SSE42-NEXT: retq
+;
+; AVX2-LABEL: usubsat_v8i32_no_narrow:
+; AVX2: # %bb.0:
+; AVX2-NEXT: vpmaxud %ymm1, %ymm0, %ymm0
+; AVX2-NEXT: vpsubd %ymm1, %ymm0, %ymm0
+; AVX2-NEXT: retq
+;
+; AVX512-LABEL: usubsat_v8i32_no_narrow:
+; AVX512: # %bb.0:
+; AVX512-NEXT: vpmaxud %ymm1, %ymm0, %ymm0
+; AVX512-NEXT: vpsubd %ymm1, %ymm0, %ymm0
+; AVX512-NEXT: retq
+ %z = call <8 x i32> @llvm.usub.sat.v8i32(<8 x i32> %x, <8 x i32> %y)
+ ret <8 x i32> %z
+}
More information about the llvm-commits
mailing list