[llvm] [SDAG] (abs (add nsw a, -b)) -> (abds a, b) (PR #175801)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 28 09:46:16 PST 2026


https://github.com/DaKnig updated https://github.com/llvm/llvm-project/pull/175801

>From f8eec3d747e98581111252ebe111b36cfb28bbc5 Mon Sep 17 00:00:00 2001
From: Natanel Hofshi <natanel.hofshi at mobileye.com>
Date: Wed, 28 Jan 2026 18:28:42 +0200
Subject: [PATCH 1/3] [SDAG] (abs (add nsw a, -b)) -> (abds a, b)

This is beneficial for constants

Note to self: we should really do something about constants and sub
turning to add...

>From a36fdfa097c1820ea91508f4008b83981b580038 Mon Sep 17 00:00:00 2001
From: Natanel Hofshi <natanel.hofshi at mobileye.com>
Date: Wed, 28 Jan 2026 17:32:00 +0200
Subject: [PATCH 2/3] lit

---
 llvm/test/CodeGen/AArch64/neon-abd.ll | 58 +++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/llvm/test/CodeGen/AArch64/neon-abd.ll b/llvm/test/CodeGen/AArch64/neon-abd.ll
index c9f3fc44ddcb1..87a88f8c8e41f 100644
--- a/llvm/test/CodeGen/AArch64/neon-abd.ll
+++ b/llvm/test/CodeGen/AArch64/neon-abd.ll
@@ -743,6 +743,64 @@ entry:
   ret <8 x i32> %r
 }
 
+define <4 x i32> @abs_sub(<4 x i32> %a, <4 x i32> %b) {
+; CHECK-LABEL: abs_sub:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    sabd v0.4s, v1.4s, v0.4s
+; CHECK-NEXT:    ret
+entry:
+  %add =  sub nsw <4 x i32> %b, %a
+  %cmp.i = icmp slt <4 x i32> %add, zeroinitializer
+  %sub.i = sub nsw <4 x i32> zeroinitializer, %add
+  %cond.i = select <4 x i1> %cmp.i, <4 x i32> %sub.i, <4 x i32> %add
+  ret <4 x i32> %cond.i
+}
+
+; short abs_diff_add_i16_rir(short a, short c) {
+;   return abs(a - 0x492) + c;
+; }
+define <4 x i16> @abs_diff_add_v4i16(<4 x i16> %a, <4 x i16> %c) {
+; CHECK-LABEL: abs_diff_add_v4i16:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov w8, #-1170 // =0xfffffb6e
+; CHECK-NEXT:    dup v2.4s, w8
+; CHECK-NEXT:    saddw v0.4s, v2.4s, v0.4h
+; CHECK-NEXT:    abs v0.4s, v0.4s
+; CHECK-NEXT:    xtn v0.4h, v0.4s
+; CHECK-NEXT:    add v0.4h, v0.4h, v1.4h
+; CHECK-NEXT:    ret
+entry:
+  %conv = sext <4 x i16> %a to <4 x i32>
+  %sub = add nsw <4 x i32> %conv, splat(i32 -1170)
+  %0 = tail call <4 x i32> @llvm.abs.v4i32(<4 x i32> %sub, i1 true)
+  %1 = trunc <4 x i32> %0 to <4 x i16>
+  %conv2 = add <4 x i16> %1, %c
+  ret <4 x i16> %conv2
+}
+
+; short abs_diff_add_<4 x i16>_rii(short a) {
+;   return abs(a - 0x93) + 0x943;
+; }
+define <4 x i16> @abs_diff_add_v4i16_rii(<4 x i16> %a) {
+; CHECK-LABEL: abs_diff_add_v4i16_rii:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mvni v1.4s, #146
+; CHECK-NEXT:    mov w8, #2371 // =0x943
+; CHECK-NEXT:    saddw v0.4s, v1.4s, v0.4h
+; CHECK-NEXT:    dup v1.4h, w8
+; CHECK-NEXT:    abs v0.4s, v0.4s
+; CHECK-NEXT:    xtn v0.4h, v0.4s
+; CHECK-NEXT:    add v0.4h, v0.4h, v1.4h
+; CHECK-NEXT:    ret
+entry:
+  %conv = sext <4 x i16> %a to <4 x i32>
+  %sub = add nsw <4 x i32> %conv, splat(i32 -147)
+  %0 = tail call <4 x i32> @llvm.abs.v4i32(<4 x i32> %sub, i1 true)
+  %1 = trunc <4 x i32> %0 to <4 x i16>
+  %conv1 = add nuw <4 x i16> %1, splat(i16 2371)
+  ret <4 x i16> %conv1
+}
+
 declare <8 x i8> @llvm.abs.v8i8(<8 x i8>, i1)
 declare <16 x i8> @llvm.abs.v16i8(<16 x i8>, i1)
 

>From b80b6ba9138c638533ca8946b8f85f26565f5b5b Mon Sep 17 00:00:00 2001
From: Natanel Hofshi <natanel.hofshi at mobileye.com>
Date: Tue, 13 Jan 2026 11:58:09 +0200
Subject: [PATCH 3/3] [SDAG] (abs (add nsw a, -b)) -> (abds a, b)

This is beneficial for constants

Note to self: we should really do something about constants and sub
turning to add...
---
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 18 +++++++++++++++---
 llvm/test/CodeGen/AArch64/neon-abd.ll         | 18 +++++++-----------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 02ca8197161a5..0a01792d1ecbc 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -11738,7 +11738,9 @@ SDValue DAGCombiner::foldABSToABD(SDNode *N, const SDLoc &DL) {
   EVT VT = N->getValueType(0);
   SDValue Op0, Op1;
 
-  if (!sd_match(N, m_Abs(m_Sub(m_Value(Op0), m_Value(Op1)))))
+  if (!sd_match(N, m_Abs(m_AnyOf(m_Sub(m_Value(Op0), m_Value(Op1)),
+                                 m_Add(m_Value(Op0),
+                                       m_AllOf(m_ConstInt(), m_Value(Op1)))))))
     return SDValue();
 
   SDValue AbsOp0 = N->getOperand(0);
@@ -11750,16 +11752,26 @@ SDValue DAGCombiner::foldABSToABD(SDNode *N, const SDLoc &DL) {
       (Opc0 != ISD::ZERO_EXTEND && Opc0 != ISD::SIGN_EXTEND &&
        Opc0 != ISD::SIGN_EXTEND_INREG)) {
     // fold (abs (sub nsw x, y)) -> abds(x, y)
+    // fold (abs (add nsw x, -y)) -> abds(x, y)
     // Don't fold this for unsupported types as we lose the NSW handling.
+    bool IsSub = AbsOp0.getOpcode() == ISD::SUB;
+    bool AbsOpWillNSW =
+        AbsOp0->getFlags().hasNoSignedWrap() ||
+        (IsSub ? DAG.willNotOverflowSub(/*IsSigned=*/true, Op0, Op1)
+               : DAG.willNotOverflowAdd(/*IsSigned=*/true, Op0, Op1));
+
     if (hasOperation(ISD::ABDS, VT) && TLI.preferABDSToABSWithNSW(VT) &&
-        (AbsOp0->getFlags().hasNoSignedWrap() ||
-         DAG.willNotOverflowSub(/*IsSigned=*/true, Op0, Op1))) {
+        AbsOpWillNSW) {
+      if (!IsSub)
+        Op1 = DAG.getNegative(Op1, SDLoc(Op1), VT);
       SDValue ABD = DAG.getNode(ISD::ABDS, DL, VT, Op0, Op1);
       return DAG.getZExtOrTrunc(ABD, DL, SrcVT);
     }
     // fold (abs (sub x, y)) -> abdu(x, y)
     if (hasOperation(ISD::ABDU, VT) && DAG.SignBitIsZero(Op0) &&
         DAG.SignBitIsZero(Op1)) {
+      if (!IsSub)
+        Op1 = DAG.getNegative(Op1, SDLoc(Op1), VT);
       SDValue ABD = DAG.getNode(ISD::ABDU, DL, VT, Op0, Op1);
       return DAG.getZExtOrTrunc(ABD, DL, SrcVT);
     }
diff --git a/llvm/test/CodeGen/AArch64/neon-abd.ll b/llvm/test/CodeGen/AArch64/neon-abd.ll
index 87a88f8c8e41f..e0406e40ff6de 100644
--- a/llvm/test/CodeGen/AArch64/neon-abd.ll
+++ b/llvm/test/CodeGen/AArch64/neon-abd.ll
@@ -762,12 +762,10 @@ entry:
 define <4 x i16> @abs_diff_add_v4i16(<4 x i16> %a, <4 x i16> %c) {
 ; CHECK-LABEL: abs_diff_add_v4i16:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov w8, #-1170 // =0xfffffb6e
-; CHECK-NEXT:    dup v2.4s, w8
-; CHECK-NEXT:    saddw v0.4s, v2.4s, v0.4h
-; CHECK-NEXT:    abs v0.4s, v0.4s
-; CHECK-NEXT:    xtn v0.4h, v0.4s
-; CHECK-NEXT:    add v0.4h, v0.4h, v1.4h
+; CHECK-NEXT:    mov w8, #1170 // =0x492
+; CHECK-NEXT:    dup v2.4h, w8
+; CHECK-NEXT:    saba v1.4h, v0.4h, v2.4h
+; CHECK-NEXT:    fmov d0, d1
 ; CHECK-NEXT:    ret
 entry:
   %conv = sext <4 x i16> %a to <4 x i32>
@@ -784,13 +782,11 @@ entry:
 define <4 x i16> @abs_diff_add_v4i16_rii(<4 x i16> %a) {
 ; CHECK-LABEL: abs_diff_add_v4i16_rii:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mvni v1.4s, #146
 ; CHECK-NEXT:    mov w8, #2371 // =0x943
-; CHECK-NEXT:    saddw v0.4s, v1.4s, v0.4h
+; CHECK-NEXT:    movi v2.4h, #147
 ; CHECK-NEXT:    dup v1.4h, w8
-; CHECK-NEXT:    abs v0.4s, v0.4s
-; CHECK-NEXT:    xtn v0.4h, v0.4s
-; CHECK-NEXT:    add v0.4h, v0.4h, v1.4h
+; CHECK-NEXT:    saba v1.4h, v0.4h, v2.4h
+; CHECK-NEXT:    fmov d0, d1
 ; CHECK-NEXT:    ret
 entry:
   %conv = sext <4 x i16> %a to <4 x i32>



More information about the llvm-commits mailing list