[llvm] beca2e9 - [AArch64] Prefer (sub x, -c) over (add x, c) if -c is cheaper. (#211020)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 23 01:13:46 PDT 2026
Author: Ricardo Jesus
Date: 2026-07-23T09:13:41+01:00
New Revision: beca2e90e6bc75f517fa4b78ac76de6141fc558f
URL: https://github.com/llvm/llvm-project/commit/beca2e90e6bc75f517fa4b78ac76de6141fc558f
DIFF: https://github.com/llvm/llvm-project/commit/beca2e90e6bc75f517fa4b78ac76de6141fc558f.diff
LOG: [AArch64] Prefer (sub x, -c) over (add x, c) if -c is cheaper. (#211020)
The negation of an immediate can be cheaper to materialise than the
original immediate, in which case a subtraction is preferable.
Added:
llvm/test/CodeGen/AArch64/add-to-sub-imm.ll
Modified:
llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
llvm/lib/Target/AArch64/AArch64InstrInfo.td
Removed:
################################################################################
diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
index 3f23e34d54c55..0862c5754da60 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "AArch64.h"
+#include "AArch64ExpandImm.h"
#include "AArch64MachineFunctionInfo.h"
#include "AArch64TargetMachine.h"
#include "MCTargetDesc/AArch64AddressingModes.h"
@@ -476,6 +477,7 @@ class AArch64DAGToDAGISel : public SelectionDAGISel {
bool SelectAddrModeXRO(SDValue N, unsigned Size, SDValue &Base,
SDValue &Offset, SDValue &SignExtend,
SDValue &DoShift);
+ bool isWorthNegatingImm(SDValue V) const;
bool isWorthFoldingALU(SDValue V, bool LSL = false) const;
bool isWorthFoldingAddr(SDValue V, unsigned Size) const;
bool SelectExtendedSHL(SDValue N, unsigned Size, bool WantExtend,
@@ -994,6 +996,25 @@ getExtendTypeForNode(SDValue N, bool IsLoadStore = false) {
return AArch64_AM::InvalidShiftExtend;
}
+/// Determine whether constant -V is cheaper to materialise than V.
+bool AArch64DAGToDAGISel::isWorthNegatingImm(SDValue V) const {
+ assert(isa<ConstantSDNode>(V) && "invalid node");
+
+ EVT VT = V.getValueType();
+ assert((VT == MVT::i32 || VT == MVT::i64) && "invalid type");
+
+ // It's only worth negating the constant if it doesn't have other uses.
+ if (!V.hasOneUse())
+ return false;
+
+ uint64_t Imm = cast<ConstantSDNode>(V)->getZExtValue();
+ unsigned BitSize = VT.getSizeInBits();
+ SmallVector<AArch64_IMM::ImmInsnModel, 4> OrigCost, NewCost;
+ AArch64_IMM::expandMOVImm(Imm, BitSize, OrigCost);
+ AArch64_IMM::expandMOVImm(-Imm, BitSize, NewCost);
+ return NewCost.size() < OrigCost.size();
+}
+
/// Determine whether it is worth to fold V into an extended register of an
/// Add/Sub. LSL means we are folding into an `add w0, w1, w2, lsl #N`
/// instruction, and the shift should be treated as worth folding even if has
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index c3ea54c1dd358..a660a3f11bdb2 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -2850,6 +2850,17 @@ def : Pat<(AArch64sub_flag GPR64:$Rn, neg_addsub_shifted_imm64:$imm),
(ADDSXri GPR64:$Rn, neg_addsub_shifted_imm64:$imm)>;
}
+def negate_imm : SDNodeXForm<imm, [{
+ return CurDAG->getTargetConstant(-N->getAPIntValue(), SDLoc(N), N->getValueType(0));
+}]>;
+def neg_cheaper_imm32 : PatLeaf<(i32 imm), [{ return isWorthNegatingImm(Op); }], negate_imm>;
+def neg_cheaper_imm64 : PatLeaf<(i64 imm), [{ return isWorthNegatingImm(Op); }], negate_imm>;
+
+// Prefer (sub x, -c) over (add x, c) if -c is cheaper to materialise than c.
+def : Pat<(add GPR32:$Rn, neg_cheaper_imm32:$imm),
+ (SUBSWrr GPR32:$Rn, (MOVi32imm neg_cheaper_imm32:$imm))>;
+def : Pat<(add GPR64:$Rn, neg_cheaper_imm64:$imm),
+ (SUBSXrr GPR64:$Rn, (MOVi64imm neg_cheaper_imm64:$imm))>;
def trunc_isWorthFoldingALU : PatFrag<(ops node:$src), (trunc $src)> {
let PredicateCode = [{ return isWorthFoldingALU(SDValue(N, 0)); }];
diff --git a/llvm/test/CodeGen/AArch64/add-to-sub-imm.ll b/llvm/test/CodeGen/AArch64/add-to-sub-imm.ll
new file mode 100644
index 0000000000000..f41394ad1c9eb
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/add-to-sub-imm.ll
@@ -0,0 +1,82 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=aarch64 < %s | FileCheck --check-prefixes=CHECK,CHECK-SD %s
+; RUN: llc -mtriple=aarch64 -global-isel -global-isel-abort=1 < %s | FileCheck --check-prefixes=CHECK,CHECK-GI %s
+
+define i32 @add_to_sub_i32(i32 %x) {
+; CHECK-SD-LABEL: add_to_sub_i32:
+; CHECK-SD: // %bb.0:
+; CHECK-SD-NEXT: mov w8, #-1431699457 // =0xaaa9ffff
+; CHECK-SD-NEXT: sub w0, w0, w8
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: add_to_sub_i32:
+; CHECK-GI: // %bb.0:
+; CHECK-GI-NEXT: mov w8, #1 // =0x1
+; CHECK-GI-NEXT: movk w8, #21846, lsl #16
+; CHECK-GI-NEXT: add w0, w0, w8
+; CHECK-GI-NEXT: ret
+ %r = add i32 %x, u0x55560001
+ ret i32 %r
+}
+
+define i64 @add_to_sub_i64(i64 %x) {
+; CHECK-SD-LABEL: add_to_sub_i64:
+; CHECK-SD: // %bb.0:
+; CHECK-SD-NEXT: mov x8, #281474976645120 // =0xffffffff0000
+; CHECK-SD-NEXT: sub x0, x0, x8
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: add_to_sub_i64:
+; CHECK-GI: // %bb.0:
+; CHECK-GI-NEXT: mov x8, #65536 // =0x10000
+; CHECK-GI-NEXT: movk x8, #65535, lsl #48
+; CHECK-GI-NEXT: add x0, x0, x8
+; CHECK-GI-NEXT: ret
+ %r = add i64 %x, u0xFFFF000000010000
+ ret i64 %r
+}
+
+define i32 @add_to_sub_cse(i32 %x) {
+; CHECK-SD-LABEL: add_to_sub_cse:
+; CHECK-SD: // %bb.0:
+; CHECK-SD-NEXT: mov w8, #-1431699457 // =0xaaa9ffff
+; CHECK-SD-NEXT: // fake_use: $w8
+; CHECK-SD-NEXT: sub w0, w0, w8
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: add_to_sub_cse:
+; CHECK-GI: // %bb.0:
+; CHECK-GI-NEXT: mov w8, #-1431699457 // =0xaaa9ffff
+; CHECK-GI-NEXT: // fake_use: $w8
+; CHECK-GI-NEXT: mov w8, #1 // =0x1
+; CHECK-GI-NEXT: movk w8, #21846, lsl #16
+; CHECK-GI-NEXT: add w0, w0, w8
+; CHECK-GI-NEXT: ret
+ tail call void (...) @llvm.fake.use(i32 u0xaaa9ffff)
+ %r = add i32 %x, u0x55560001
+ ret i32 %r
+}
+
+define i32 @keep_add_same_cost(i32 %x) {
+; CHECK-LABEL: keep_add_same_cost:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov w8, #22136 // =0x5678
+; CHECK-NEXT: movk w8, #4660, lsl #16
+; CHECK-NEXT: add w0, w0, w8
+; CHECK-NEXT: ret
+ %r = add i32 %x, u0x12345678
+ ret i32 %r
+}
+
+define i32 @keep_add_multiuse(i32 %x) {
+; CHECK-LABEL: keep_add_multiuse:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov w8, #1 // =0x1
+; CHECK-NEXT: movk w8, #21846, lsl #16
+; CHECK-NEXT: // fake_use: $w8
+; CHECK-NEXT: add w0, w0, w8
+; CHECK-NEXT: ret
+ tail call void (...) @llvm.fake.use(i32 u0x55560001)
+ %r = add i32 %x, u0x55560001
+ ret i32 %r
+}
More information about the llvm-commits
mailing list