[llvm] [X86] Optimize USUBSAT <N x i64> to vpsubusb (PR #196122)

Tharun V K via llvm-commits llvm-commits at lists.llvm.org
Wed May 6 10:17:58 PDT 2026


https://github.com/tharunvk created https://github.com/llvm/llvm-project/pull/196122

This fix implements a targeted optimization for llvm.usub.sat on x86 AVX-512 targets. When the operands of a vector saturating subtraction are known to fit within the lower 8 bits (e.g., after a zext(trunc(x)) or an and v, 255 pattern), we can narrow the operation from 64-bit (or 32/16-bit) lanes down to 8-bit lanes.

Fixes https://github.com/llvm/llvm-project/issues/195462

>From bd5c78fc5c99332d3dba2e56f03f0ed4a2bb9f4f Mon Sep 17 00:00:00 2001
From: Tharun V K <tharunms98 at gmail.com>
Date: Wed, 6 May 2026 22:35:06 +0530
Subject: [PATCH] [X86] Optimize USUBSAT <N x i64> to vpsubusb

---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 56 +++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 5a1171b2b4ee6..353d74737b197 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -29723,6 +29723,57 @@ static SDValue lowerAddSub(SDValue Op, SelectionDAG &DAG,
   return splitVectorIntBinary(Op, DAG, DL);
 }
 
+static SDValue lowerUSUBSATTo8Bit(SDValue Op, SelectionDAG &DAG) {
+  EVT VT = Op.getValueType();
+
+  if (!VT.isVector() || VT.getScalarSizeInBits() <= 8)
+    return SDValue();
+
+  SDValue N0 = Op.getOperand(0);
+  SDValue N1 = Op.getOperand(1);
+
+  if (!ISD::isBuildVectorOfConstantSDNodes(N1.getNode()))
+    return SDValue();
+
+  unsigned ScalarSize = VT.getScalarSizeInBits();
+  unsigned RequiredZeros = ScalarSize - 8;
+
+  KnownBits Known0 = DAG.computeKnownBits(N0);
+  if (Known0.countMinLeadingZeros() < RequiredZeros)
+    return SDValue();
+
+  KnownBits Known1 = DAG.computeKnownBits(N1);
+  if (Known1.countMinLeadingZeros() < RequiredZeros)
+    return SDValue();
+
+  SDLoc DL(Op);
+  EVT ByteVT =
+      EVT::getVectorVT(*DAG.getContext(), MVT::i8, VT.getSizeInBits() / 8);
+  EVT ByteScalarVT = ByteVT.getScalarType();
+
+  SmallVector<SDValue, 64> NewConsts;
+  unsigned NumElts = VT.getVectorNumElements();
+  unsigned BytesPerElt = ScalarSize / 8;
+
+  for (unsigned i = 0; i < NumElts; ++i) {
+    auto *CNode = cast<ConstantSDNode>(N1.getOperand(i));
+    uint64_t Val = CNode->getZExtValue();
+
+    NewConsts.push_back(DAG.getConstant(Val & 0xFF, DL, ByteScalarVT));
+
+    for (unsigned j = 1; j < BytesPerElt; ++j) {
+      NewConsts.push_back(DAG.getConstant(0xFF, DL, ByteScalarVT));
+    }
+  }
+
+  SDValue NewN1 = DAG.getBuildVector(ByteVT, DL, NewConsts);
+  SDValue BitcastN0 = DAG.getBitcast(ByteVT, N0);
+
+  SDValue Sub = DAG.getNode(ISD::USUBSAT, DL, ByteVT, BitcastN0, NewN1);
+
+  return DAG.getBitcast(VT, Sub);
+}
+
 static SDValue LowerADDSAT_SUBSAT(SDValue Op, SelectionDAG &DAG,
                                   const X86Subtarget &Subtarget) {
   MVT VT = Op.getSimpleValueType();
@@ -29730,6 +29781,11 @@ static SDValue LowerADDSAT_SUBSAT(SDValue Op, SelectionDAG &DAG,
   unsigned Opcode = Op.getOpcode();
   SDLoc DL(Op);
 
+  if (Opcode == ISD::USUBSAT && Op.getValueType().isVector()) {
+    if (SDValue Res = lowerUSUBSATTo8Bit(Op, DAG))
+      return Res;
+  }
+
   if (VT == MVT::v32i16 || VT == MVT::v64i8 ||
       (VT.is256BitVector() && !Subtarget.hasInt256())) {
     assert(Op.getSimpleValueType().isInteger() &&



More information about the llvm-commits mailing list