[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