[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