[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