[llvm] [AArch64] Select sqdmulh for smul.fix.sat with scale == eltbits-1 (PR #211579)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 23 08:15:21 PDT 2026


https://github.com/guyfischman created https://github.com/llvm/llvm-project/pull/211579

Follow-up to #209351 (which vectorized the generic expansion): on AArch64, smul.fix.sat with scale == eltbits-1 is exactly sqdmulh, so select it directly instead of the generic clamp. Split out per @davemgreen's request.

I used AI to split this out of https://github.com/llvm/llvm-project/pull/209351, and reviewed it.

>From 11bf7c4c6ed04f74a30f42227508ca6f134c7b5a Mon Sep 17 00:00:00 2001
From: Guy Fischman <gfischman at gmail.com>
Date: Thu, 23 Jul 2026 14:50:09 +0200
Subject: [PATCH] [AArch64] Select sqdmulh for smul.fix.sat with scale ==
 eltbits-1

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 18 ++++++++++
 llvm/lib/Target/AArch64/AArch64ISelLowering.h |  1 +
 llvm/test/CodeGen/AArch64/smul_fix_sat.ll     | 36 +++++++++++++++++++
 3 files changed, 55 insertions(+)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 213a0042461ae..f7afe7979936c 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1461,6 +1461,9 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
     setOperationAction(ISD::VECREDUCE_OR, MVT::v2i64, Custom);
     setOperationAction(ISD::VECREDUCE_XOR, MVT::v2i64, Custom);
 
+    for (MVT VT : {MVT::v4i16, MVT::v8i16, MVT::v2i32, MVT::v4i32})
+      setOperationAction(ISD::SMULFIXSAT, VT, Custom);
+
     setOperationAction(ISD::ANY_EXTEND, MVT::v4i32, Legal);
     setTruncStoreAction(MVT::v2i32, MVT::v2i16, Expand);
     // Likewise, narrowing and extending vector loads/stores aren't handled
@@ -8134,6 +8137,19 @@ SDValue AArch64TargetLowering::LowerVECTOR_COMPRESS(SDValue Op,
                      Passthru);
 }
 
+SDValue AArch64TargetLowering::LowerSMULFIXSAT(SDValue Op,
+                                               SelectionDAG &DAG) const {
+  EVT VT = Op.getValueType();
+  unsigned Scale = Op.getConstantOperandVal(2);
+  if (Scale != VT.getScalarSizeInBits() - 1)
+    return SDValue();
+
+  SDLoc DL(Op);
+  SDValue ID = DAG.getConstant(Intrinsic::aarch64_neon_sqdmulh, DL, MVT::i32);
+  return DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, VT, ID, Op.getOperand(0),
+                     Op.getOperand(1));
+}
+
 // Generate SUBS and CSEL for integer abs.
 SDValue AArch64TargetLowering::LowerABS(SDValue Op, SelectionDAG &DAG) const {
   MVT VT = Op.getSimpleValueType();
@@ -8848,6 +8864,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op,
     return LowerFixedLengthVectorSelectToSVE(Op, DAG);
   case ISD::ABS:
     return LowerABS(Op, DAG);
+  case ISD::SMULFIXSAT:
+    return LowerSMULFIXSAT(Op, DAG);
   case ISD::ABDS:
     return LowerToPredicatedOp(Op, DAG, AArch64ISD::ABDS_PRED);
   case ISD::ABDU:
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 9c2ea0faee5ec..d6573f61bf8d9 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -657,6 +657,7 @@ class AArch64TargetLowering : public TargetLowering {
   SDValue LowerSTORE(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerStore128(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerABS(SDValue Op, SelectionDAG &DAG) const;
+  SDValue LowerSMULFIXSAT(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerFMUL(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerFMA(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerCLMUL(SDValue Op, SelectionDAG &DAG) const;
diff --git a/llvm/test/CodeGen/AArch64/smul_fix_sat.ll b/llvm/test/CodeGen/AArch64/smul_fix_sat.ll
index 1d85853f7fa8d..ff10116579e98 100644
--- a/llvm/test/CodeGen/AArch64/smul_fix_sat.ll
+++ b/llvm/test/CodeGen/AArch64/smul_fix_sat.ll
@@ -411,6 +411,42 @@ define <4 x i64> @vec_v4i64(<4 x i64> %x, <4 x i64> %y) {
   ret <4 x i64> %tmp
 }
 
+define <8 x i16> @vec_sqdmulh_v8i16(<8 x i16> %x, <8 x i16> %y) nounwind {
+; CHECK-LABEL: vec_sqdmulh_v8i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sqdmulh v0.8h, v0.8h, v1.8h
+; CHECK-NEXT:    ret
+  %tmp = call <8 x i16> @llvm.smul.fix.sat.v8i16(<8 x i16> %x, <8 x i16> %y, i32 15)
+  ret <8 x i16> %tmp
+}
+
+define <4 x i16> @vec_sqdmulh_v4i16(<4 x i16> %x, <4 x i16> %y) nounwind {
+; CHECK-LABEL: vec_sqdmulh_v4i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sqdmulh v0.4h, v0.4h, v1.4h
+; CHECK-NEXT:    ret
+  %tmp = call <4 x i16> @llvm.smul.fix.sat.v4i16(<4 x i16> %x, <4 x i16> %y, i32 15)
+  ret <4 x i16> %tmp
+}
+
+define <4 x i32> @vec_sqdmulh_v4i32(<4 x i32> %x, <4 x i32> %y) nounwind {
+; CHECK-LABEL: vec_sqdmulh_v4i32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sqdmulh v0.4s, v0.4s, v1.4s
+; CHECK-NEXT:    ret
+  %tmp = call <4 x i32> @llvm.smul.fix.sat.v4i32(<4 x i32> %x, <4 x i32> %y, i32 31)
+  ret <4 x i32> %tmp
+}
+
+define <2 x i32> @vec_sqdmulh_v2i32(<2 x i32> %x, <2 x i32> %y) nounwind {
+; CHECK-LABEL: vec_sqdmulh_v2i32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    sqdmulh v0.2s, v0.2s, v1.2s
+; CHECK-NEXT:    ret
+  %tmp = call <2 x i32> @llvm.smul.fix.sat.v2i32(<2 x i32> %x, <2 x i32> %y, i32 31)
+  ret <2 x i32> %tmp
+}
+
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; CHECK-GI: {{.*}}
 ; CHECK-SD: {{.*}}



More information about the llvm-commits mailing list