[llvm] [AArch64] Decompose constant multiplies used only by memory addresses (PR #194584)

Jiang Ning via llvm-commits llvm-commits at lists.llvm.org
Tue May 12 00:25:26 PDT 2026


https://github.com/JiangNingHX updated https://github.com/llvm/llvm-project/pull/194584

>From 00f6ba663aa93a8553a0c76bfea76bf484f7a5cc Mon Sep 17 00:00:00 2001
From: JiangNing <jiangninghx at foxmail.com>
Date: Tue, 28 Apr 2026 16:50:56 +0800
Subject: [PATCH] [AArch64] Decompose address-only constant multiplies

Allow the existing constant-multiply decomposition to run when the multiply's
single ADD/SUB user is only used as an unindexed load/store address. This keeps
the MADD/MSUB-preserving bailout for arithmetic uses, while exposing ADD/LSL
forms that can be folded into AArch64 register-offset addressing modes.
---
 .../Target/AArch64/AArch64ISelLowering.cpp    |  40 ++++-
 .../AArch64/mul-const-addressing-mode.ll      | 137 ++++++++++++++++++
 2 files changed, 174 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/mul-const-addressing-mode.ll

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index d8ba83dd1d584..3ccecc3c3575c 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -20433,6 +20433,37 @@ static SDValue performVectorExtCombine(SDNode *N, SelectionDAG &DAG) {
   return SDValue();
 }
 
+static bool isOnlyUsedAsMemoryAddress(SDNode *N) {
+  assert((N->getOpcode() == ISD::ADD || N->getOpcode() == ISD::SUB) &&
+         "Expected add/sub node");
+
+  for (SDUse &Use : N->uses()) {
+    SDNode *User = Use.getUser();
+    switch (User->getOpcode()) {
+    case ISD::LOAD: {
+      auto *Load = cast<LoadSDNode>(User);
+      if (!Load->isUnindexed() || Use.getOperandNo() != 1)
+        return false;
+      break;
+    }
+    case ISD::STORE: {
+      auto *Store = cast<StoreSDNode>(User);
+      if (!Store->isUnindexed() || Use.getOperandNo() != 2)
+        return false;
+      break;
+    }
+    case AArch64ISD::PREFETCH:
+      if (Use.getOperandNo() != 2)
+        return false;
+      break;
+    default:
+      return false;
+    }
+  }
+
+  return true;
+}
+
 static SDValue performMulCombine(SDNode *N, SelectionDAG &DAG,
                                  TargetLowering::DAGCombinerInfo &DCI,
                                  const AArch64Subtarget *Subtarget) {
@@ -20518,9 +20549,12 @@ static SDValue performMulCombine(SDNode *N, SelectionDAG &DAG,
       return SDValue();
     // Conservatively do not lower to shift+add+shift if the mul might be
     // folded into madd or msub.
-    if (N->hasOneUse() && (N->user_begin()->getOpcode() == ISD::ADD ||
-                           N->user_begin()->getOpcode() == ISD::SUB))
-      return SDValue();
+    if (N->hasOneUse()) {
+      SDNode *User = *N->user_begin();
+      if ((User->getOpcode() == ISD::ADD || User->getOpcode() == ISD::SUB) &&
+          !isOnlyUsedAsMemoryAddress(User))
+        return SDValue();
+    }
   }
   // Use ShiftedConstValue instead of ConstValue to support both shift+add/sub
   // and shift+add+shift.
diff --git a/llvm/test/CodeGen/AArch64/mul-const-addressing-mode.ll b/llvm/test/CodeGen/AArch64/mul-const-addressing-mode.ll
new file mode 100644
index 0000000000000..2ff4e4f7b223d
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/mul-const-addressing-mode.ll
@@ -0,0 +1,137 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=aarch64 | FileCheck %s
+
+define i32 @ldrw_3x(ptr %p, i64 %x) {
+; CHECK-LABEL: ldrw_3x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #1
+; CHECK-NEXT:    ldr w0, [x0, x8, lsl #2]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 12
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  %v = load i32, ptr %addr, align 4
+  ret i32 %v
+}
+
+define i32 @ldrw_5x(ptr %p, i64 %x) {
+; CHECK-LABEL: ldrw_5x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #2
+; CHECK-NEXT:    ldr w0, [x0, x8, lsl #2]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 20
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  %v = load i32, ptr %addr, align 4
+  ret i32 %v
+}
+
+define i32 @ldrw_6x(ptr %p, i64 %x) {
+; CHECK-LABEL: ldrw_6x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #1
+; CHECK-NEXT:    lsl x8, x8, #3
+; CHECK-NEXT:    ldr w0, [x0, x8]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 24
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  %v = load i32, ptr %addr, align 4
+  ret i32 %v
+}
+
+define void @strw_3x(ptr %p, i64 %x, i32 %v) {
+; CHECK-LABEL: strw_3x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #1
+; CHECK-NEXT:    str w2, [x0, x8, lsl #2]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 12
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  store i32 %v, ptr %addr, align 4
+  ret void
+}
+
+define i64 @ldrx_3x(ptr %p, i64 %x) {
+; CHECK-LABEL: ldrx_3x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #1
+; CHECK-NEXT:    ldr x0, [x0, x8, lsl #3]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 24
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  %v = load i64, ptr %addr, align 8
+  ret i64 %v
+}
+
+define i64 @ldrx_5x(ptr %p, i64 %x) {
+; CHECK-LABEL: ldrx_5x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #2
+; CHECK-NEXT:    ldr x0, [x0, x8, lsl #3]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 40
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  %v = load i64, ptr %addr, align 8
+  ret i64 %v
+}
+
+define i64 @ldrx_6x(ptr %p, i64 %x) {
+; CHECK-LABEL: ldrx_6x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #1
+; CHECK-NEXT:    lsl x8, x8, #4
+; CHECK-NEXT:    ldr x0, [x0, x8]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 48
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  %v = load i64, ptr %addr, align 8
+  ret i64 %v
+}
+
+define void @strx_3x(ptr %p, i64 %x, i64 %v) {
+; CHECK-LABEL: strx_3x:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    add x8, x1, x1, lsl #1
+; CHECK-NEXT:    str x2, [x0, x8, lsl #3]
+; CHECK-NEXT:    ret
+  %off = mul i64 %x, 24
+  %addr = getelementptr inbounds i8, ptr %p, i64 %off
+  store i64 %v, ptr %addr, align 8
+  ret void
+}
+
+define void @store_add_as_value_and_addr(i64 %x, i64 %y) {
+; CHECK-LABEL: store_add_as_value_and_addr:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #24 // =0x18
+; CHECK-NEXT:    mul x8, x0, x8
+; CHECK-NEXT:    add x9, x8, x1
+; CHECK-NEXT:    str x9, [x8, x1]
+; CHECK-NEXT:    ret
+  %mul = mul i64 %x, 24
+  %add = add i64 %mul, %y
+  %addr = inttoptr i64 %add to ptr
+  store i64 %add, ptr %addr, align 8
+  ret void
+}
+
+define i64 @arithmetic_mul_add(i64 %x, i64 %y) {
+; CHECK-LABEL: arithmetic_mul_add:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #24 // =0x18
+; CHECK-NEXT:    madd x0, x0, x8, x1
+; CHECK-NEXT:    ret
+  %mul = mul i64 %x, 24
+  %add = add i64 %mul, %y
+  ret i64 %add
+}
+
+define i64 @arithmetic_mul_sub(i64 %x, i64 %y) {
+; CHECK-LABEL: arithmetic_mul_sub:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #24 // =0x18
+; CHECK-NEXT:    msub x0, x0, x8, x1
+; CHECK-NEXT:    ret
+  %mul = mul i64 %x, 24
+  %sub = sub i64 %y, %mul
+  ret i64 %sub
+}



More information about the llvm-commits mailing list