[llvm] [AArch64][GlobalISel] Add tablegen patterns for smull+sqshrn -> sqdmulh. (PR #217318)
David Green via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 02:39:50 PDT 2026
https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/217318
>From dea7959aa39d9b30ea0b932ac48182dce43b875b Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Thu, 27 Aug 2026 10:39:25 +0100
Subject: [PATCH] [AArch64][GlobalISel] Add tablegen patterns for smull+sqshrn
-> sqdmulh.
We do this in parts in globalisel, recognizing the smull and trunc_sat, which
when combined with the correct shift can fold into a single sqdmulh.
---
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 21 ++++++
.../CodeGen/AArch64/saturating-vec-smull.ll | 70 +++++--------------
llvm/test/CodeGen/AArch64/smul_fix_sat.ll | 62 +++++-----------
3 files changed, 55 insertions(+), 98 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index e876ef7597454..16d4a26032b22 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -10903,6 +10903,27 @@ def : Pat<(v8i16 (AArch64sqdmulh (v8i16 V128:$Rn), (v8i16 V128:$Rm))),
def : Pat<(v4i32 (AArch64sqdmulh (v4i32 V128:$Rn), (v4i32 V128:$Rm))),
(SQDMULHv4i32 V128:$Rn, V128:$Rm)>;
+def AArch64sqdmulhPat : PatFrag<(ops node:$Rn, node:$Rm, node:$offset),
+ (truncssat_s (AArch64vashr (AArch64smull node:$Rn, node:$Rm), node:$offset))>;
+def : Pat<(v4i16 (AArch64sqdmulhPat (v4i16 V64:$Rn), (v4i16 V64:$Rm), (i32 15))),
+ (SQDMULHv4i16 V64:$Rn, V64:$Rm)>;
+def : Pat<(v2i32 (AArch64sqdmulhPat (v2i32 V64:$Rn), (v2i32 V64:$Rm), (i32 31))),
+ (SQDMULHv2i32 V64:$Rn, V64:$Rm)>;
+def : Pat<(v8i16 (concat_vectors (v4i16 (AArch64sqdmulhPat (v4i16 (extract_subvector V128:$Rn, (i64 0))),
+ (v4i16 (extract_subvector V128:$Rm, (i64 0))),
+ (i32 15))),
+ (v4i16 (AArch64sqdmulhPat (v4i16 (extract_subvector V128:$Rn, (i64 4))),
+ (v4i16 (extract_subvector V128:$Rm, (i64 4))),
+ (i32 15))))),
+ (SQDMULHv8i16 V128:$Rn, V128:$Rm)>;
+def : Pat<(v4i32 (concat_vectors (v2i32 (AArch64sqdmulhPat (v2i32 (extract_subvector V128:$Rn, (i64 0))),
+ (v2i32 (extract_subvector V128:$Rm, (i64 0))),
+ (i32 31))),
+ (v2i32 (AArch64sqdmulhPat (v2i32 (extract_subvector V128:$Rn, (i64 2))),
+ (v2i32 (extract_subvector V128:$Rm, (i64 2))),
+ (i32 31))))),
+ (SQDMULHv4i32 V128:$Rn, V128:$Rm)>;
+
// Conversions within AdvSIMD types in the same register size are free.
// But because we need a consistent lane ordering, in big endian many
// conversions require one or more REV instructions.
diff --git a/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll b/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll
index a16eb65b32a71..c92ad65c5c810 100644
--- a/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll
+++ b/llvm/test/CodeGen/AArch64/saturating-vec-smull.ll
@@ -35,16 +35,10 @@ define <2 x i16> @saturating_2xi16(<2 x i16> %a, <2 x i16> %b) {
}
define <4 x i16> @saturating_4xi16(<4 x i16> %a, <4 x i16> %b) {
-; CHECK-SD-LABEL: saturating_4xi16:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.4h, v1.4h, v0.4h
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: saturating_4xi16:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v0.4s, v1.4h, v0.4h
-; CHECK-GI-NEXT: sqshrn v0.4h, v0.4s, #15
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: saturating_4xi16:
+; CHECK: // %bb.0:
+; CHECK-NEXT: sqdmulh v0.4h, v1.4h, v0.4h
+; CHECK-NEXT: ret
%as = sext <4 x i16> %a to <4 x i32>
%bs = sext <4 x i16> %b to <4 x i32>
%m = mul nsw <4 x i32> %bs, %as
@@ -55,18 +49,10 @@ define <4 x i16> @saturating_4xi16(<4 x i16> %a, <4 x i16> %b) {
}
define <8 x i16> @saturating_8xi16(<8 x i16> %a, <8 x i16> %b) {
-; CHECK-SD-LABEL: saturating_8xi16:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.8h, v1.8h, v0.8h
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: saturating_8xi16:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v2.4s, v1.4h, v0.4h
-; CHECK-GI-NEXT: smull2 v1.4s, v1.8h, v0.8h
-; CHECK-GI-NEXT: sqshrn v0.4h, v2.4s, #15
-; CHECK-GI-NEXT: sqshrn2 v0.8h, v1.4s, #15
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: saturating_8xi16:
+; CHECK: // %bb.0:
+; CHECK-NEXT: sqdmulh v0.8h, v1.8h, v0.8h
+; CHECK-NEXT: ret
%as = sext <8 x i16> %a to <8 x i32>
%bs = sext <8 x i16> %b to <8 x i32>
%m = mul nsw <8 x i32> %bs, %as
@@ -77,16 +63,10 @@ define <8 x i16> @saturating_8xi16(<8 x i16> %a, <8 x i16> %b) {
}
define <2 x i32> @saturating_2xi32(<2 x i32> %a, <2 x i32> %b) {
-; CHECK-SD-LABEL: saturating_2xi32:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.2s, v1.2s, v0.2s
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: saturating_2xi32:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v0.2d, v1.2s, v0.2s
-; CHECK-GI-NEXT: sqshrn v0.2s, v0.2d, #31
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: saturating_2xi32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: sqdmulh v0.2s, v1.2s, v0.2s
+; CHECK-NEXT: ret
%as = sext <2 x i32> %a to <2 x i64>
%bs = sext <2 x i32> %b to <2 x i64>
%m = mul nsw <2 x i64> %bs, %as
@@ -97,18 +77,10 @@ define <2 x i32> @saturating_2xi32(<2 x i32> %a, <2 x i32> %b) {
}
define <4 x i32> @saturating_4xi32(<4 x i32> %a, <4 x i32> %b) {
-; CHECK-SD-LABEL: saturating_4xi32:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.4s, v1.4s, v0.4s
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: saturating_4xi32:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v2.2d, v1.2s, v0.2s
-; CHECK-GI-NEXT: smull2 v1.2d, v1.4s, v0.4s
-; CHECK-GI-NEXT: sqshrn v0.2s, v2.2d, #31
-; CHECK-GI-NEXT: sqshrn2 v0.4s, v1.2d, #31
-; CHECK-GI-NEXT: ret
+; CHECK-LABEL: saturating_4xi32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: sqdmulh v0.4s, v1.4s, v0.4s
+; CHECK-NEXT: ret
%as = sext <4 x i32> %a to <4 x i64>
%bs = sext <4 x i32> %b to <4 x i64>
%m = mul nsw <4 x i64> %bs, %as
@@ -127,14 +99,8 @@ define <8 x i32> @saturating_8xi32(<8 x i32> %a, <8 x i32> %b) {
;
; CHECK-GI-LABEL: saturating_8xi32:
; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v4.2d, v2.2s, v0.2s
-; CHECK-GI-NEXT: smull v5.2d, v3.2s, v1.2s
-; CHECK-GI-NEXT: smull2 v2.2d, v2.4s, v0.4s
-; CHECK-GI-NEXT: smull2 v3.2d, v3.4s, v1.4s
-; CHECK-GI-NEXT: sqshrn v0.2s, v4.2d, #31
-; CHECK-GI-NEXT: sqshrn v1.2s, v5.2d, #31
-; CHECK-GI-NEXT: sqshrn2 v0.4s, v2.2d, #31
-; CHECK-GI-NEXT: sqshrn2 v1.4s, v3.2d, #31
+; CHECK-GI-NEXT: sqdmulh v0.4s, v2.4s, v0.4s
+; CHECK-GI-NEXT: sqdmulh v1.4s, v3.4s, v1.4s
; CHECK-GI-NEXT: ret
%as = sext <8 x i32> %a to <8 x i64>
%bs = sext <8 x i32> %b to <8 x i64>
diff --git a/llvm/test/CodeGen/AArch64/smul_fix_sat.ll b/llvm/test/CodeGen/AArch64/smul_fix_sat.ll
index 865b11100c4f5..744d1be126ff1 100644
--- a/llvm/test/CodeGen/AArch64/smul_fix_sat.ll
+++ b/llvm/test/CodeGen/AArch64/smul_fix_sat.ll
@@ -790,68 +790,38 @@ define <4 x i64> @vec_v4i64(<4 x i64> %x, <4 x i64> %y) {
}
define <8 x i16> @vec_sqdmulh_v8i16(<8 x i16> %x, <8 x i16> %y) {
-; CHECK-SD-LABEL: vec_sqdmulh_v8i16:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.8h, v0.8h, v1.8h
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: vec_sqdmulh_v8i16:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v2.4s, v0.4h, v1.4h
-; CHECK-GI-NEXT: smull2 v1.4s, v0.8h, v1.8h
-; CHECK-GI-NEXT: sqshrn v0.4h, v2.4s, #15
-; CHECK-GI-NEXT: sqshrn2 v0.8h, v1.4s, #15
-; CHECK-GI-NEXT: ret
+; 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) {
-; CHECK-SD-LABEL: vec_sqdmulh_v4i16:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.4h, v0.4h, v1.4h
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: vec_sqdmulh_v4i16:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v0.4s, v0.4h, v1.4h
-; CHECK-GI-NEXT: sqshrn v0.4h, v0.4s, #15
-; CHECK-GI-NEXT: ret
+; 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) {
-; CHECK-SD-LABEL: vec_sqdmulh_v4i32:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.4s, v0.4s, v1.4s
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: vec_sqdmulh_v4i32:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v2.2d, v0.2s, v1.2s
-; CHECK-GI-NEXT: smull2 v1.2d, v0.4s, v1.4s
-; CHECK-GI-NEXT: sqshrn v0.2s, v2.2d, #31
-; CHECK-GI-NEXT: sqshrn2 v0.4s, v1.2d, #31
-; CHECK-GI-NEXT: ret
+; 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) {
-; CHECK-SD-LABEL: vec_sqdmulh_v2i32:
-; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: sqdmulh v0.2s, v0.2s, v1.2s
-; CHECK-SD-NEXT: ret
-;
-; CHECK-GI-LABEL: vec_sqdmulh_v2i32:
-; CHECK-GI: // %bb.0:
-; CHECK-GI-NEXT: smull v0.2d, v0.2s, v1.2s
-; CHECK-GI-NEXT: sqshrn v0.2s, v0.2d, #31
-; CHECK-GI-NEXT: ret
+; 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: {{.*}}
More information about the llvm-commits
mailing list