[llvm] [AArch64][SelectionDAG] Generate subs+csel for usub.sat (PR #193203)

Shreeyash Pandey via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 02:29:39 PDT 2026


https://github.com/bojle updated https://github.com/llvm/llvm-project/pull/193203

>From 678480aa3a27730be2376397362032965c116c95 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shrpand at qti.qualcomm.com>
Date: Tue, 21 Apr 2026 04:50:45 -0700
Subject: [PATCH 1/3] [AArch64][SelectionDAG] Generate subs+csel for usub.sat

Fixes https://github.com/llvm/llvm-project/issues/191488

As this is a regression of
https://github.com/llvm/llvm-project/pull/170076, adds a check to avoid
generic lowering of usub.sat to X - zext(X != 0) in case of aarch64 via
a virtual hook in TargetLowering. All other backends will still receive
generic lowering as implemented in the original patch.

Change-Id: I0a194bcc9e66819c12d0f9179464823301f0d7bf
---
 llvm/include/llvm/CodeGen/TargetLowering.h     |  4 ++++
 .../CodeGen/SelectionDAG/TargetLowering.cpp    |  3 ++-
 .../lib/Target/AArch64/AArch64ISelLowering.cpp |  5 +++++
 llvm/lib/Target/AArch64/AArch64ISelLowering.h  |  2 ++
 llvm/test/CodeGen/AArch64/and-mask-removal.ll  |  6 +++---
 llvm/test/CodeGen/AArch64/usub_sat_plus.ll     | 18 ++++++++++++++++++
 6 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 8aff327ee0e76..a5b1162bacd13 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -3596,6 +3596,10 @@ class LLVM_ABI TargetLoweringBase {
     return false;
   }
 
+  /// Should usub.sat(X, 1) prefer the generic lowering X - zext(X != 0) over
+  /// the default overflow/select expansion?
+  virtual bool preferSubOfZextForUsubSatOne(EVT VT) const { return true; }
+
   /// True if target has some particular form of dealing with pointer arithmetic
   /// semantics for pointers with the given value type. False if pointer
   /// arithmetic should not be preserved for passes such as instruction
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 2f069e7c1c90d..59562921d9340 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -11475,7 +11475,8 @@ SDValue TargetLowering::expandAddSubSat(SDNode *Node, SelectionDAG &DAG) const {
   }
 
   // usub.sat(a, 1) -> sub(a, zext(a != 0))
-  if (Opcode == ISD::USUBSAT && isOneOrOneSplat(RHS)) {
+  if (Opcode == ISD::USUBSAT && isOneOrOneSplat(RHS) &&
+      preferSubOfZextForUsubSatOne(VT)) {
     LHS = DAG.getFreeze(LHS);
     SDValue Zero = DAG.getConstant(0, dl, VT);
     EVT BoolVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 0ea5b2986e9c0..383d01afd09bb 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -31035,6 +31035,11 @@ bool AArch64TargetLowering::shouldConvertFpToSat(unsigned Op, EVT FPVT,
   return TargetLowering::shouldConvertFpToSat(Op, FPVT, VT);
 }
 
+bool AArch64TargetLowering::preferSubOfZextForUsubSatOne(EVT /*VT*/) const {
+  // See https://github.com/llvm/llvm-project/issues/191488
+  return false;
+}
+
 bool AArch64TargetLowering::preferSelectsOverBooleanArithmetic(EVT VT) const {
   // Expand scalar and SVE operations using selects. Neon vectors prefer sub to
   // avoid vselect becoming bsl / unrolling.
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 58efdd3e18fc0..cdef09ef7013e 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -450,6 +450,8 @@ class AArch64TargetLowering : public TargetLowering {
 
   bool shouldConvertFpToSat(unsigned Op, EVT FPVT, EVT VT) const override;
 
+  bool preferSubOfZextForUsubSatOne(EVT VT) const override;
+
   bool preferSelectsOverBooleanArithmetic(EVT VT) const override;
 
   bool isComplexDeinterleavingSupported() const override;
diff --git a/llvm/test/CodeGen/AArch64/and-mask-removal.ll b/llvm/test/CodeGen/AArch64/and-mask-removal.ll
index 855fe5caf97b2..5046c0571ad2b 100644
--- a/llvm/test/CodeGen/AArch64/and-mask-removal.ll
+++ b/llvm/test/CodeGen/AArch64/and-mask-removal.ll
@@ -483,9 +483,9 @@ define i64 @pr58109(i8 signext %0) {
 ; CHECK-SD-LABEL: pr58109:
 ; CHECK-SD:       ; %bb.0:
 ; CHECK-SD-NEXT:    add w8, w0, #1
-; CHECK-SD-NEXT:    ands w8, w8, #0xff
-; CHECK-SD-NEXT:    cset w9, ne
-; CHECK-SD-NEXT:    sub w0, w8, w9
+; CHECK-SD-NEXT:    and w8, w8, #0xff
+; CHECK-SD-NEXT:    subs w8, w8, #1
+; CHECK-SD-NEXT:    csel w0, wzr, w8, lo
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: pr58109:
diff --git a/llvm/test/CodeGen/AArch64/usub_sat_plus.ll b/llvm/test/CodeGen/AArch64/usub_sat_plus.ll
index 2793aeb163c94..9f1e2eeb04781 100644
--- a/llvm/test/CodeGen/AArch64/usub_sat_plus.ll
+++ b/llvm/test/CodeGen/AArch64/usub_sat_plus.ll
@@ -8,6 +8,24 @@ declare i16 @llvm.usub.sat.i16(i16, i16)
 declare i32 @llvm.usub.sat.i32(i32, i32)
 declare i64 @llvm.usub.sat.i64(i64, i64)
 
+define i32 @sat_dec_i32(i32 %x) nounwind {
+; CHECK-SD-LABEL: sat_dec_i32:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    subs w8, w0, #1
+; CHECK-SD-NEXT:    csel w0, wzr, w8, lo
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: sat_dec_i32:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    subs w8, w0, #1
+; CHECK-GI-NEXT:    cset w9, lo
+; CHECK-GI-NEXT:    tst w9, #0x1
+; CHECK-GI-NEXT:    csel w0, wzr, w8, ne
+; CHECK-GI-NEXT:    ret
+  %tmp = call i32 @llvm.usub.sat.i32(i32 %x, i32 1)
+  ret i32 %tmp
+}
+
 define i32 @func32(i32 %x, i32 %y, i32 %z) nounwind {
 ; CHECK-SD-LABEL: func32:
 ; CHECK-SD:       // %bb.0:

>From eb99c5c5342185db4e9e213477baa9071d56dedb Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shrpand at qti.qualcomm.com>
Date: Wed, 22 Apr 2026 06:09:36 -0700
Subject: [PATCH 2/3] redo with isOperationLegalOrCustom

Change-Id: I4575a604fe5a76be2e657c63707c5fe25d631b98
---
 llvm/include/llvm/CodeGen/TargetLowering.h       | 4 ----
 llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp | 3 ++-
 llvm/lib/Target/AArch64/AArch64ISelLowering.cpp  | 5 -----
 llvm/lib/Target/AArch64/AArch64ISelLowering.h    | 2 --
 4 files changed, 2 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index a5b1162bacd13..8aff327ee0e76 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -3596,10 +3596,6 @@ class LLVM_ABI TargetLoweringBase {
     return false;
   }
 
-  /// Should usub.sat(X, 1) prefer the generic lowering X - zext(X != 0) over
-  /// the default overflow/select expansion?
-  virtual bool preferSubOfZextForUsubSatOne(EVT VT) const { return true; }
-
   /// True if target has some particular form of dealing with pointer arithmetic
   /// semantics for pointers with the given value type. False if pointer
   /// arithmetic should not be preserved for passes such as instruction
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 59562921d9340..2447b18e8b327 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -11475,8 +11475,9 @@ SDValue TargetLowering::expandAddSubSat(SDNode *Node, SelectionDAG &DAG) const {
   }
 
   // usub.sat(a, 1) -> sub(a, zext(a != 0))
+  // Prefer this on targets without legal/cost-effective overflow-carry nodes.
   if (Opcode == ISD::USUBSAT && isOneOrOneSplat(RHS) &&
-      preferSubOfZextForUsubSatOne(VT)) {
+      !isOperationLegalOrCustom(ISD::USUBO_CARRY, VT)) {
     LHS = DAG.getFreeze(LHS);
     SDValue Zero = DAG.getConstant(0, dl, VT);
     EVT BoolVT = getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 383d01afd09bb..0ea5b2986e9c0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -31035,11 +31035,6 @@ bool AArch64TargetLowering::shouldConvertFpToSat(unsigned Op, EVT FPVT,
   return TargetLowering::shouldConvertFpToSat(Op, FPVT, VT);
 }
 
-bool AArch64TargetLowering::preferSubOfZextForUsubSatOne(EVT /*VT*/) const {
-  // See https://github.com/llvm/llvm-project/issues/191488
-  return false;
-}
-
 bool AArch64TargetLowering::preferSelectsOverBooleanArithmetic(EVT VT) const {
   // Expand scalar and SVE operations using selects. Neon vectors prefer sub to
   // avoid vselect becoming bsl / unrolling.
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index cdef09ef7013e..58efdd3e18fc0 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -450,8 +450,6 @@ class AArch64TargetLowering : public TargetLowering {
 
   bool shouldConvertFpToSat(unsigned Op, EVT FPVT, EVT VT) const override;
 
-  bool preferSubOfZextForUsubSatOne(EVT VT) const override;
-
   bool preferSelectsOverBooleanArithmetic(EVT VT) const override;
 
   bool isComplexDeinterleavingSupported() const override;

>From a5fc2248b1e7cb24f7138cedeb2314f5c86cb666 Mon Sep 17 00:00:00 2001
From: Shreeyash Pandey <shrpand at qti.qualcomm.com>
Date: Wed, 22 Apr 2026 12:59:15 -0700
Subject: [PATCH 3/3] regen combine-sub-usat.ll

Change-Id: If34cdfe1eea8c30f6be502a63270ae5b6c34d141
---
 llvm/test/CodeGen/X86/combine-sub-usat.ll | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/X86/combine-sub-usat.ll b/llvm/test/CodeGen/X86/combine-sub-usat.ll
index 7ec4e062930db..e601c5733bd42 100644
--- a/llvm/test/CodeGen/X86/combine-sub-usat.ll
+++ b/llvm/test/CodeGen/X86/combine-sub-usat.ll
@@ -116,9 +116,9 @@ define <8 x i16> @combine_zero_v8i16(<8 x i16> %a0) {
 define i32 @combine_dec_i32(i32 %a0) {
 ; CHECK-LABEL: combine_dec_i32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    movl %edi, %eax
-; CHECK-NEXT:    cmpl $1, %edi
-; CHECK-NEXT:    adcl $-1, %eax
+; CHECK-NEXT:    xorl %eax, %eax
+; CHECK-NEXT:    subl $1, %edi
+; CHECK-NEXT:    cmovael %edi, %eax
 ; CHECK-NEXT:    retq
   %1 = call i32 @llvm.usub.sat.i32(i32 %a0, i32 1)
   ret i32 %1



More information about the llvm-commits mailing list