[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