[llvm] [ARM][MVE] Match canonical saturating negation patterns (PR #213085)

Ties Stuij via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 31 02:30:17 PDT 2026


https://github.com/stuij updated https://github.com/llvm/llvm-project/pull/213085

>From 4e177818e64fe74ef2d3d33a71412cbb0dcf6064 Mon Sep 17 00:00:00 2001
From: Ties Stuij <ties.stuij at arm.com>
Date: Thu, 30 Jul 2026 13:55:00 +0100
Subject: [PATCH 1/2] [ARM][MVE] Match canonical saturating negation patterns

InstCombine pull request #194519 canonicalized the saturating negation idiom to
an llvm.ssub.sat operation. For the ARM backend we only recognized the original
select and subtract pattern, causing vector absolute values to expand to VQSUB
plus a compare and select instead of VQABS.

In this patch we teach the VQABS and VQNEG patterns to recognize this ssub.sat
form.
---
 llvm/lib/Target/ARM/ARMInstrMVE.td | 11 +++++++++++
 llvm/test/CodeGen/Thumb2/vqabs.ll  | 15 +++------------
 llvm/test/CodeGen/Thumb2/vqneg.ll  |  9 +++------
 3 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Target/ARM/ARMInstrMVE.td b/llvm/lib/Target/ARM/ARMInstrMVE.td
index 38f36cb239535..6b4feb638c9e3 100644
--- a/llvm/lib/Target/ARM/ARMInstrMVE.td
+++ b/llvm/lib/Target/ARM/ARMInstrMVE.td
@@ -2568,6 +2568,14 @@ multiclass vqabsneg_pattern<MVEVectorVTInfo VTI, dag int_min, dag int_max,
                                 int_max,
                                 (sub (VTI.Vec zero_vec), (VTI.Vec MQPR:$reg)))))),
             (VTI.Vec (vqabs_instruction (VTI.Vec MQPR:$reg)))>;
+    // InstCombine canonicalizes the saturating negate in the expression above
+    // to ssub.sat(0, r).
+    def : Pat<(VTI.Vec (vselect
+                      (VTI.Pred (ARMvcmpz (VTI.Vec MQPR:$reg), ARMCCgt)),
+                      (VTI.Vec MQPR:$reg),
+                      (VTI.Vec (ssubsat (VTI.Vec zero_vec),
+                                        (VTI.Vec MQPR:$reg))))),
+              (VTI.Vec (vqabs_instruction (VTI.Vec MQPR:$reg)))>;
     // Similarly, this tree represents vqneg, i.e. the following vectorized expression:
     // r == INT_MIN ? INT_MAX : -r
     def : Pat<(VTI.Vec (vselect
@@ -2575,6 +2583,9 @@ multiclass vqabsneg_pattern<MVEVectorVTInfo VTI, dag int_min, dag int_max,
                         int_max,
                         (sub (VTI.Vec zero_vec), (VTI.Vec MQPR:$reg)))),
                (VTI.Vec (vqneg_instruction (VTI.Vec MQPR:$reg)))>;
+    def : Pat<(VTI.Vec (ssubsat (VTI.Vec zero_vec),
+                              (VTI.Vec MQPR:$reg))),
+              (VTI.Vec (vqneg_instruction (VTI.Vec MQPR:$reg)))>;
   }
 }
 
diff --git a/llvm/test/CodeGen/Thumb2/vqabs.ll b/llvm/test/CodeGen/Thumb2/vqabs.ll
index f8f4b65a1f7dc..832d85b1095c4 100644
--- a/llvm/test/CodeGen/Thumb2/vqabs.ll
+++ b/llvm/test/CodeGen/Thumb2/vqabs.ll
@@ -52,10 +52,7 @@ entry:
 define arm_aapcs_vfpcc <16 x i8> @vqabs_sat_test16(<16 x i8> %A) {
 ; CHECK-LABEL: vqabs_sat_test16:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vmov.i32 q1, #0x0
-; CHECK-NEXT:    vcmp.s8 gt, q0, zr
-; CHECK-NEXT:    vqsub.s8 q1, q1, q0
-; CHECK-NEXT:    vpsel q0, q0, q1
+; CHECK-NEXT:    vqabs.s8 q0, q0
 ; CHECK-NEXT:    bx lr
 entry:
   %0 = icmp sgt <16 x i8> %A, zeroinitializer
@@ -67,10 +64,7 @@ entry:
 define arm_aapcs_vfpcc <8 x i16> @vqabs_sat_test8(<8 x i16> %A) {
 ; CHECK-LABEL: vqabs_sat_test8:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vmov.i32 q1, #0x0
-; CHECK-NEXT:    vcmp.s16 gt, q0, zr
-; CHECK-NEXT:    vqsub.s16 q1, q1, q0
-; CHECK-NEXT:    vpsel q0, q0, q1
+; CHECK-NEXT:    vqabs.s16 q0, q0
 ; CHECK-NEXT:    bx lr
 entry:
   %0 = icmp sgt <8 x i16> %A, zeroinitializer
@@ -82,10 +76,7 @@ entry:
 define arm_aapcs_vfpcc <4 x i32> @vqabs_sat_test4(<4 x i32> %A) {
 ; CHECK-LABEL: vqabs_sat_test4:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vmov.i32 q1, #0x0
-; CHECK-NEXT:    vcmp.s32 gt, q0, zr
-; CHECK-NEXT:    vqsub.s32 q1, q1, q0
-; CHECK-NEXT:    vpsel q0, q0, q1
+; CHECK-NEXT:    vqabs.s32 q0, q0
 ; CHECK-NEXT:    bx lr
 entry:
   %0 = icmp sgt <4 x i32> %A, zeroinitializer
diff --git a/llvm/test/CodeGen/Thumb2/vqneg.ll b/llvm/test/CodeGen/Thumb2/vqneg.ll
index 4f357d81a5fc5..3a7c227bf4d22 100644
--- a/llvm/test/CodeGen/Thumb2/vqneg.ll
+++ b/llvm/test/CodeGen/Thumb2/vqneg.ll
@@ -46,8 +46,7 @@ entry:
 define arm_aapcs_vfpcc <16 x i8> @vqneg_sat_test16(<16 x i8> %A) {
 ; CHECK-LABEL: vqneg_sat_test16:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vmov.i32 q1, #0x0
-; CHECK-NEXT:    vqsub.s8 q0, q1, q0
+; CHECK-NEXT:    vqneg.s8 q0, q0
 ; CHECK-NEXT:    bx lr
 entry:
   %0 = tail call <16 x i8> @llvm.ssub.sat.v16i8(<16 x i8> zeroinitializer, <16 x i8> %A)
@@ -57,8 +56,7 @@ entry:
 define arm_aapcs_vfpcc <8 x i16> @vqneg_sat_test8(<8 x i16> %A) {
 ; CHECK-LABEL: vqneg_sat_test8:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vmov.i32 q1, #0x0
-; CHECK-NEXT:    vqsub.s16 q0, q1, q0
+; CHECK-NEXT:    vqneg.s16 q0, q0
 ; CHECK-NEXT:    bx lr
 entry:
   %0 = tail call <8 x i16> @llvm.ssub.sat.v8i16(<8 x i16> zeroinitializer, <8 x i16> %A)
@@ -68,8 +66,7 @@ entry:
 define arm_aapcs_vfpcc <4 x i32> @vqneg_sat_test4(<4 x i32> %A) {
 ; CHECK-LABEL: vqneg_sat_test4:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vmov.i32 q1, #0x0
-; CHECK-NEXT:    vqsub.s32 q0, q1, q0
+; CHECK-NEXT:    vqneg.s32 q0, q0
 ; CHECK-NEXT:    bx lr
 entry:
   %0 = tail call <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> zeroinitializer, <4 x i32> %A)

>From a86f2fda439d93ec97b45f43a1bde838b70d7cc0 Mon Sep 17 00:00:00 2001
From: Ties Stuij <ties.stuij at arm.com>
Date: Fri, 31 Jul 2026 10:29:20 +0100
Subject: [PATCH 2/2] Minor formatting change.

---
 llvm/lib/Target/ARM/ARMInstrMVE.td | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/ARM/ARMInstrMVE.td b/llvm/lib/Target/ARM/ARMInstrMVE.td
index 6b4feb638c9e3..6a94a5615e1c7 100644
--- a/llvm/lib/Target/ARM/ARMInstrMVE.td
+++ b/llvm/lib/Target/ARM/ARMInstrMVE.td
@@ -2573,8 +2573,7 @@ multiclass vqabsneg_pattern<MVEVectorVTInfo VTI, dag int_min, dag int_max,
     def : Pat<(VTI.Vec (vselect
                       (VTI.Pred (ARMvcmpz (VTI.Vec MQPR:$reg), ARMCCgt)),
                       (VTI.Vec MQPR:$reg),
-                      (VTI.Vec (ssubsat (VTI.Vec zero_vec),
-                                        (VTI.Vec MQPR:$reg))))),
+                      (VTI.Vec (ssubsat (VTI.Vec zero_vec), (VTI.Vec MQPR:$reg))))),
               (VTI.Vec (vqabs_instruction (VTI.Vec MQPR:$reg)))>;
     // Similarly, this tree represents vqneg, i.e. the following vectorized expression:
     // r == INT_MIN ? INT_MAX : -r
@@ -2583,8 +2582,7 @@ multiclass vqabsneg_pattern<MVEVectorVTInfo VTI, dag int_min, dag int_max,
                         int_max,
                         (sub (VTI.Vec zero_vec), (VTI.Vec MQPR:$reg)))),
                (VTI.Vec (vqneg_instruction (VTI.Vec MQPR:$reg)))>;
-    def : Pat<(VTI.Vec (ssubsat (VTI.Vec zero_vec),
-                              (VTI.Vec MQPR:$reg))),
+    def : Pat<(VTI.Vec (ssubsat (VTI.Vec zero_vec), (VTI.Vec MQPR:$reg))),
               (VTI.Vec (vqneg_instruction (VTI.Vec MQPR:$reg)))>;
   }
 }



More information about the llvm-commits mailing list