[llvm] [ARM][MVE] Match canonical saturating negation patterns (PR #213085)
Ties Stuij via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 30 10:47:57 PDT 2026
https://github.com/stuij updated https://github.com/llvm/llvm-project/pull/213085
>From a041b1d5335bcec45649fdba90f012fa6e877a0e 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] [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)
More information about the llvm-commits
mailing list