[llvm] [AArch64] fuse constant addition after sbb (PR #185117)
Takashi Idobe via llvm-commits
llvm-commits at lists.llvm.org
Sat Mar 7 13:13:16 PST 2026
https://github.com/Takashiidobe updated https://github.com/llvm/llvm-project/pull/185117
>From cab53e68763f99b7bc1619904a89076cd42a84bb Mon Sep 17 00:00:00 2001
From: Takashiidobe <idobetakashi at gmail.com>
Date: Fri, 6 Mar 2026 16:48:02 -0500
Subject: [PATCH 1/2] fuse constant addition after sbc for arm only
---
.../Target/AArch64/AArch64ISelLowering.cpp | 33 ++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index cd9de6c729649..5ed109b298e08 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -23346,6 +23346,36 @@ static SDValue performAddTruncShiftCombine(SDNode *N, SelectionDAG &DAG) {
return DAG.getNode(ISD::ADD, DL, VT, Trunc, Shift);
}
+// Fold ADD(SBC(Y, 0, W), C) -> SBC(Y, -C, W)
+// SBC(Y, 0, W) = Y - 0 - ~carry = Y + carry - 1
+// Adding C: Y + carry - 1 + C = Y - (-C) - ~carry = SBC(Y, -C, W)
+static SDValue performAddWithSBCCombine(SDNode *N, SelectionDAG &DAG) {
+ if (N->getOpcode() != ISD::ADD)
+ return SDValue();
+ EVT VT = N->getValueType(0);
+ if (VT != MVT::i32 && VT != MVT::i64)
+ return SDValue();
+
+ SDValue SBC = N->getOperand(0);
+ SDValue C = N->getOperand(1);
+ // ADD is commutative; constant may be on either side.
+ if (SBC.getOpcode() != AArch64ISD::SBC)
+ std::swap(SBC, C);
+ if (SBC.getOpcode() != AArch64ISD::SBC || !SBC.hasOneUse())
+ return SDValue();
+ if (!isNullConstant(SBC.getOperand(1)))
+ return SDValue();
+ // AArch64 SBC (non-flag-setting) has only one output; no flags guard needed.
+ auto *CC = dyn_cast<ConstantSDNode>(C);
+ if (!CC)
+ return SDValue();
+
+ SDLoc DL(N);
+ return DAG.getNode(AArch64ISD::SBC, DL, VT, SBC.getOperand(0),
+ DAG.getConstant(-CC->getAPIntValue(), DL, VT),
+ SBC.getOperand(2));
+}
+
static SDValue performAddSubCombine(SDNode *N,
TargetLowering::DAGCombinerInfo &DCI) {
// Try to change sum of two reductions.
@@ -23371,7 +23401,8 @@ static SDValue performAddSubCombine(SDNode *N,
return Val;
if (SDValue Val = performAddTruncShiftCombine(N, DCI.DAG))
return Val;
-
+ if (SDValue Val = performAddWithSBCCombine(N, DCI.DAG))
+ return Val;
if (SDValue Val = performExtBinopLoadFold(N, DCI.DAG))
return Val;
>From a85776c33b655e645077bb84476510a11b6fcc1a Mon Sep 17 00:00:00 2001
From: Takashiidobe <idobetakashi at gmail.com>
Date: Sat, 7 Mar 2026 10:08:13 -0500
Subject: [PATCH 2/2] add positive and negative tests for SBC fold on arm64
---
llvm/test/CodeGen/AArch64/sbc-add-constant.ll | 60 +++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 llvm/test/CodeGen/AArch64/sbc-add-constant.ll
diff --git a/llvm/test/CodeGen/AArch64/sbc-add-constant.ll b/llvm/test/CodeGen/AArch64/sbc-add-constant.ll
new file mode 100644
index 0000000000000..016091bbf28e7
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sbc-add-constant.ll
@@ -0,0 +1,60 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck %s
+;
+; Verify that ADD(SBB(Y,0,flags),C) folds to SBB(Y,-C,flags).
+; SBB(Y,0) = Y - CF; adding C gives Y - CF + C = Y - (-C) - CF = SBB(Y,-C).
+
+declare {i64, i1} @llvm.usub.with.overflow.i64(i64, i64)
+
+; Fold should fire, adding with constant
+define i64 @g_i64(i64 %a, i64 %b) nounwind {
+; CHECK-LABEL: g_i64:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov x8, #-10 // =0xfffffffffffffff6
+; CHECK-NEXT: subs x9, x0, x1
+; CHECK-NEXT: sbc x0, x9, x8
+; CHECK-NEXT: ret
+ %ov = call {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
+ %val = extractvalue { i64, i1 } %ov, 0
+ %bit = extractvalue { i64, i1 } %ov, 1
+ %ext = sext i1 %bit to i64
+ %r = add i64 %val, %ext
+ %r2 = add i64 %r, 10
+ ret i64 %r2
+}
+
+; Non-constant addend should not generate the fold
+define i64 @g_nonconstant(i64 %a, i64 %b, i64 %c) nounwind {
+; CHECK-LABEL: g_nonconstant:
+; CHECK: // %bb.0:
+; CHECK-NEXT: subs x8, x0, x1
+; CHECK-NEXT: sbc x8, x8, xzr
+; CHECK-NEXT: add x0, x8, x2
+; CHECK-NEXT: ret
+ %ov = call {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
+ %val = extractvalue { i64, i1 } %ov, 0
+ %bit = extractvalue { i64, i1 } %ov, 1
+ %ext = sext i1 %bit to i64
+ %r = add i64 %val, %ext
+ %r2 = add i64 %r, %c
+ ret i64 %r2
+}
+
+; Multiple uses of SBC result should not generate the fold
+define i64 @g_multi_use(i64 %a, i64 %b, ptr %out) nounwind {
+; CHECK-LABEL: g_multi_use:
+; CHECK: // %bb.0:
+; CHECK-NEXT: subs x8, x0, x1
+; CHECK-NEXT: sbc x8, x8, xzr
+; CHECK-NEXT: add x0, x8, #10
+; CHECK-NEXT: str x8, [x2]
+; CHECK-NEXT: ret
+ %ov = call {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)
+ %val = extractvalue { i64, i1 } %ov, 0
+ %bit = extractvalue { i64, i1 } %ov, 1
+ %ext = sext i1 %bit to i64
+ %sbc = add i64 %val, %ext
+ store i64 %sbc, ptr %out
+ %r = add i64 %sbc, 10
+ ret i64 %r
+}
More information about the llvm-commits
mailing list