[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