[llvm] [AArch64][SelectionDAG] Reduce redundant loads for constant <2 x i64> values (PR #214864)

Ethan Luis McDonough via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 11 00:23:04 PDT 2026


https://github.com/EthanLuisMcDonough updated https://github.com/llvm/llvm-project/pull/214864

>From 28a1c6ae138fbbd877deda57e0052f426f1d2b07 Mon Sep 17 00:00:00 2001
From: Ethan Luis McDonough <emcdonou at qti.qualcomm.com>
Date: Fri, 7 Aug 2026 15:00:09 -0700
Subject: [PATCH 1/2] Fix #38518

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 29 +++++++++++++++++++
 .../test/CodeGen/AArch64/v2i64-const-store.ll | 25 ++++++++++++++++
 2 files changed, 54 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/v2i64-const-store.ll

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 2ac6c5fbc471a..56d543423a368 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27597,6 +27597,35 @@ static SDValue performSTORECombine(SDNode *N,
     }
   }
 
+  // For loads that read constant <2 x i64> values, use two MOVZs and a stp
+  // instruction if both i64 values fit inside 16 bits.
+  if (ST->getOperand(1).getValueType() == MVT::v2i64 &&
+      ST->getOperand(1).getNumOperands() == 2) {
+    auto *FirstVal =
+        dyn_cast<ConstantSDNode>(ST->getOperand(1).getOperand(0).getNode());
+    auto *SecondVal =
+        dyn_cast<ConstantSDNode>(ST->getOperand(1).getOperand(1).getNode());
+    if (FirstVal && SecondVal &&
+        FirstVal->getZExtValue() <= std::numeric_limits<uint16_t>::max() &&
+        SecondVal->getZExtValue() <= std::numeric_limits<uint16_t>::max()) {
+      auto GenImmMov = [&](uint64_t Imm) {
+        return Imm == 0 ? DAG.getRegister(AArch64::XZR, MVT::i64)
+                        : SDValue(DAG.getMachineNode(
+                                      AArch64::MOVZXi, DL, MVT::i64,
+                                      DAG.getTargetConstant(Imm, DL, MVT::i16),
+                                      DAG.getTargetConstant(0, DL, MVT::i64)),
+                                  0);
+      };
+      auto FV = GenImmMov(FirstVal->getZExtValue());
+      auto SV = GenImmMov(SecondVal->getZExtValue());
+      if (DAG.getDataLayout().isBigEndian())
+        std::swap(FV, SV);
+      return DAG.getMemIntrinsicNode(
+          AArch64ISD::STP, DL, DAG.getVTList(MVT::Other), {Chain, FV, SV, Ptr},
+          MemVT, ST->getMemOperand());
+    }
+  }
+
   // This is an integer vector_extract_elt followed by a (possibly truncating)
   // store. We may be able to replace this with a store of an FP subregister.
   if (DCI.isAfterLegalizeDAG() && ST->isUnindexed() &&
diff --git a/llvm/test/CodeGen/AArch64/v2i64-const-store.ll b/llvm/test/CodeGen/AArch64/v2i64-const-store.ll
new file mode 100644
index 0000000000000..70170ba28fb40
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/v2i64-const-store.ll
@@ -0,0 +1,25 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O3 -verify-machineinstrs -mtriple=aarch64-unknown-linux | FileCheck %s
+
+define void @foo(ptr %s) {
+; CHECK-LABEL: foo:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov x8, #1 // =0x1
+; CHECK-NEXT:    stp x8, xzr, [x0]
+; CHECK-NEXT:    ret
+entry:
+  store <2 x i64> <i64 1, i64 0>, ptr %s, align 8
+  ret void
+}
+
+define void @foo2(ptr %s) {
+; CHECK-LABEL: foo2:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    mov x8, #10293 // =0x2835
+; CHECK-NEXT:    mov x9, #2329 // =0x919
+; CHECK-NEXT:    stp x9, x8, [x0]
+; CHECK-NEXT:    ret
+entry:
+  store <2 x i64> <i64 2329, i64 10293>, ptr %s, align 8
+  ret void
+}

>From d0ee6191364c5e4ae33a0f99d668c991f2828a51 Mon Sep 17 00:00:00 2001
From: Ethan Luis McDonough <emcdonou at qti.qualcomm.com>
Date: Tue, 11 Aug 2026 00:16:27 -0700
Subject: [PATCH 2/2] Refactor transformation logic

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 30 ++++++-------------
 .../test/CodeGen/AArch64/v2i64-const-store.ll |  6 ++--
 2 files changed, 12 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 56d543423a368..b91d5ce457014 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27597,27 +27597,15 @@ static SDValue performSTORECombine(SDNode *N,
     }
   }
 
-  // For loads that read constant <2 x i64> values, use two MOVZs and a stp
-  // instruction if both i64 values fit inside 16 bits.
-  if (ST->getOperand(1).getValueType() == MVT::v2i64 &&
-      ST->getOperand(1).getNumOperands() == 2) {
-    auto *FirstVal =
-        dyn_cast<ConstantSDNode>(ST->getOperand(1).getOperand(0).getNode());
-    auto *SecondVal =
-        dyn_cast<ConstantSDNode>(ST->getOperand(1).getOperand(1).getNode());
-    if (FirstVal && SecondVal &&
-        FirstVal->getZExtValue() <= std::numeric_limits<uint16_t>::max() &&
-        SecondVal->getZExtValue() <= std::numeric_limits<uint16_t>::max()) {
-      auto GenImmMov = [&](uint64_t Imm) {
-        return Imm == 0 ? DAG.getRegister(AArch64::XZR, MVT::i64)
-                        : SDValue(DAG.getMachineNode(
-                                      AArch64::MOVZXi, DL, MVT::i64,
-                                      DAG.getTargetConstant(Imm, DL, MVT::i16),
-                                      DAG.getTargetConstant(0, DL, MVT::i64)),
-                                  0);
-      };
-      auto FV = GenImmMov(FirstVal->getZExtValue());
-      auto SV = GenImmMov(SecondVal->getZExtValue());
+  // For stores that save constant <2 x i64> values, use two mov instructions
+  // and a stp instruction if both i64 are elligable mov immediate values.
+  if (Value.getValueType() == MVT::v2i64 && Value.getNumOperands() == 2) {
+    auto FV = Value.getOperand(0);
+    auto SV = Value.getOperand(1);
+    auto *FC = dyn_cast<ConstantSDNode>(FV.getNode());
+    auto *SC = dyn_cast<ConstantSDNode>(SV.getNode());
+    if (FC && SC && AArch64_AM::isAnyMOVWMovAlias(FC->getZExtValue(), 64) &&
+        AArch64_AM::isAnyMOVWMovAlias(SC->getZExtValue(), 64)) {
       if (DAG.getDataLayout().isBigEndian())
         std::swap(FV, SV);
       return DAG.getMemIntrinsicNode(
diff --git a/llvm/test/CodeGen/AArch64/v2i64-const-store.ll b/llvm/test/CodeGen/AArch64/v2i64-const-store.ll
index 70170ba28fb40..47446c45d6097 100644
--- a/llvm/test/CodeGen/AArch64/v2i64-const-store.ll
+++ b/llvm/test/CodeGen/AArch64/v2i64-const-store.ll
@@ -4,7 +4,7 @@
 define void @foo(ptr %s) {
 ; CHECK-LABEL: foo:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov x8, #1 // =0x1
+; CHECK-NEXT:    mov w8, #1 // =0x1
 ; CHECK-NEXT:    stp x8, xzr, [x0]
 ; CHECK-NEXT:    ret
 entry:
@@ -15,8 +15,8 @@ entry:
 define void @foo2(ptr %s) {
 ; CHECK-LABEL: foo2:
 ; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mov x8, #10293 // =0x2835
-; CHECK-NEXT:    mov x9, #2329 // =0x919
+; CHECK-NEXT:    mov w8, #10293 // =0x2835
+; CHECK-NEXT:    mov w9, #2329 // =0x919
 ; CHECK-NEXT:    stp x9, x8, [x0]
 ; CHECK-NEXT:    ret
 entry:



More information about the llvm-commits mailing list