[llvm] [BOLT][AArch64] Add support for LDR relaxtion on LDRSl/LDRDl/LDRQl (PR #196983)

Shanzhi Chen via llvm-commits llvm-commits at lists.llvm.org
Mon May 11 22:01:58 PDT 2026


https://github.com/chenshanzhi updated https://github.com/llvm/llvm-project/pull/196983

>From 3262544258e3301ae457c3e69be52745ba191607 Mon Sep 17 00:00:00 2001
From: Shanzhi Chen <chenshanzhi at huawei.com>
Date: Mon, 11 May 2026 23:17:06 +0800
Subject: [PATCH] [BOLT][AArch64] Add support for LDR relaxtion on
 LDRSl/LDRDl/LDRQl

AArch64 currently has 6 `LoadLiteral` instructions:
- 3 with a GPR as the destination register (LDRWl, LDRXl, LDRSWl)
- 3 with an FPR as the destination register (LDRSl, LDRDl, LDRQl)

BOLT already supports LDR relaxation for the first three instructions.
For the latter three, the destination register cannot be used to hold the base
address directly. Therefore LivenessAnalysis can be used to obtain usable
registers for LDR relaxation, allowing BOLT to support LDR relaxation for
these instructions as well.

In addition, a hidden option is added to control the LDR relaxation for
LDRSl/LDRDl/LDRQl because LivenessAnalysis may be expensive and may not
always find usable registers for a specific ProgramPoint.
---
 bolt/include/bolt/Core/MCPlusBuilder.h        |  8 ++-
 .../bolt/Passes/AArch64RelaxationPass.h       |  6 ++
 bolt/lib/Passes/AArch64RelaxationPass.cpp     | 39 +++++++++--
 .../Target/AArch64/AArch64MCPlusBuilder.cpp   | 30 ++++++--
 bolt/test/AArch64/ldr-relaxation.s            | 69 +++++++++++++++++++
 5 files changed, 140 insertions(+), 12 deletions(-)

diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 3a840615bec8f..8d7c39d05bb02 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -866,6 +866,11 @@ class MCPlusBuilder {
     return false;
   }
 
+  virtual bool isLoadLiteralFPR(const MCInst &Inst) const {
+    llvm_unreachable("not implemented");
+    return false;
+  }
+
   virtual bool isMOVW(const MCInst &Inst) const {
     llvm_unreachable("not implemented");
     return false;
@@ -1852,7 +1857,8 @@ class MCPlusBuilder {
   ///     adrp x0, PageBase(label)
   ///     ldr  x0, [x0, PageOffset(label)]
   virtual InstructionListType createAdrpLdr(const MCInst &LDRInst,
-                                            MCContext *Ctx) const {
+                                            MCContext *Ctx,
+                                            MCPhysReg Reg = 0) const {
     llvm_unreachable("not implemented");
   }
 
diff --git a/bolt/include/bolt/Passes/AArch64RelaxationPass.h b/bolt/include/bolt/Passes/AArch64RelaxationPass.h
index b9185a1e34388..e140ff0a0af01 100644
--- a/bolt/include/bolt/Passes/AArch64RelaxationPass.h
+++ b/bolt/include/bolt/Passes/AArch64RelaxationPass.h
@@ -23,7 +23,13 @@
 namespace llvm {
 namespace bolt {
 
+class RegAnalysis;
+class BinaryFunctionCallGraph;
+
 class AArch64RelaxationPass : public BinaryFunctionPass {
+  std::unique_ptr<RegAnalysis> RA;
+  std::unique_ptr<BinaryFunctionCallGraph> CG;
+
 public:
   explicit AArch64RelaxationPass(const cl::opt<bool> &PrintPass)
       : BinaryFunctionPass(PrintPass) {}
diff --git a/bolt/lib/Passes/AArch64RelaxationPass.cpp b/bolt/lib/Passes/AArch64RelaxationPass.cpp
index 2b7384dc848dd..313349898fe55 100644
--- a/bolt/lib/Passes/AArch64RelaxationPass.cpp
+++ b/bolt/lib/Passes/AArch64RelaxationPass.cpp
@@ -11,7 +11,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "bolt/Passes/AArch64RelaxationPass.h"
+#include "bolt/Core/BinaryFunctionCallGraph.h"
 #include "bolt/Core/ParallelUtilities.h"
+#include "bolt/Passes/DataflowInfoManager.h"
 #include "bolt/Utils/CommandLineOpts.h"
 #include <iterator>
 
@@ -24,6 +26,12 @@ static cl::opt<bool> AArch64PassOpt(
     "aarch64-relaxation",
     cl::desc("Replace ARM non-local ADR/LDR instructions with ADRP"),
     cl::init(true), cl::cat(BoltCategory), cl::ReallyHidden);
+
+static cl::opt<bool> AArch64RelaxLoadLiteralFPR(
+    "aarch64-relax-ldr-fp-using-la",
+    cl::desc("Replace AArch64 LDR (literal, SIMD&FP) with ADRP, "
+             "using LivenessAnalysis to find available regsiters"),
+    cl::init(true), cl::cat(BoltCategory), cl::Hidden);
 } // namespace opts
 
 namespace llvm {
@@ -44,9 +52,10 @@ void AArch64RelaxationPass::runOnFunction(BinaryFunction &BF) {
     for (auto It = BB.begin(); It != BB.end(); ++It) {
       MCInst &Inst = *It;
       bool IsADR = BC.MIB->isADR(Inst);
-
-      // TODO: Handle other types of LDR (literal, PC-relative) instructions.
-      if (!IsADR && !BC.MIB->isLoadLiteralGPR(Inst))
+      bool IsLoadLiteralGPR = BC.MIB->isLoadLiteralGPR(Inst);
+      bool IsLoadLiteralFPR =
+          (opts::AArch64RelaxLoadLiteralFPR && BC.MIB->isLoadLiteralFPR(Inst));
+      if (!IsADR && !IsLoadLiteralGPR && !IsLoadLiteralFPR)
         continue;
 
       const MCSymbol *Symbol = BC.MIB->getTargetSymbol(Inst, IsADR ? 0 : 1);
@@ -77,9 +86,27 @@ void AArch64RelaxationPass::runOnFunction(BinaryFunction &BF) {
       InstructionListType AdrpMaterialization;
       {
         auto L = BC.scopeLock();
-        AdrpMaterialization =
-            IsADR ? BC.MIB->undoAdrpAddRelaxation(Inst, BC.Ctx.get())
-                  : BC.MIB->createAdrpLdr(Inst, BC.Ctx.get());
+        if (IsADR) {
+          AdrpMaterialization =
+              BC.MIB->undoAdrpAddRelaxation(Inst, BC.Ctx.get());
+        } else if (IsLoadLiteralGPR) {
+          AdrpMaterialization = BC.MIB->createAdrpLdr(Inst, BC.Ctx.get());
+        } else if (IsLoadLiteralFPR) {
+          if (!RA) {
+            CG.reset(new BinaryFunctionCallGraph(buildCallGraph(BC)));
+            RA.reset(new RegAnalysis(BC, &BC.getBinaryFunctions(), &*CG));
+          }
+          DataflowInfoManager Info(BF, RA.get(), nullptr);
+          auto Reg = Info.getLivenessAnalysis().scavengeRegAfter(&Inst);
+          if (Reg == 0) {
+            BC.outs()
+                << "BOLT-INFO: For lack of a register to hold the address, "
+                << "this LDR (literal, SIMD&FP) cannot be relaxed:\n";
+            BC.printInstruction(BC.errs(), Inst);
+            continue;
+          }
+          AdrpMaterialization = BC.MIB->createAdrpLdr(Inst, BC.Ctx.get(), Reg);
+        }
       }
 
       if (It != BB.begin() && BC.MIB->isNoop(*std::prev(It))) {
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 6695198802006..03eb70a0fd7c4 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -721,6 +721,12 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
            OpCode == AArch64::LDRSWl;
   }
 
+  bool isLoadLiteralFPR(const MCInst &Inst) const override {
+    unsigned OpCode = Inst.getOpcode();
+    return OpCode == AArch64::LDRSl || OpCode == AArch64::LDRDl ||
+           OpCode == AArch64::LDRQl;
+  }
+
   MCPhysReg getADRReg(const MCInst &Inst) const {
     assert((isADR(Inst) || isADRP(Inst)) && "Not an ADR instruction");
     assert(MCPlus::getNumPrimeOperands(Inst) != 0 &&
@@ -740,10 +746,8 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
     return materializeAddress(Target, Ctx, Reg, Addend);
   }
 
-  InstructionListType createAdrpLdr(const MCInst &LDRInst,
-                                    MCContext *Ctx) const override {
-    assert(isLoadLiteralGPR(LDRInst) &&
-           "LDR (literal) or LDRSW (literal) expected");
+  InstructionListType createAdrpLdr(const MCInst &LDRInst, MCContext *Ctx,
+                                    MCPhysReg Reg) const override {
     assert(LDRInst.getOperand(0).isReg() &&
            "unexpected operand in LDR instruction");
     const MCPhysReg DataReg = LDRInst.getOperand(0).getReg();
@@ -768,8 +772,24 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
       OpCode = AArch64::LDRSWui;
       RelType = ELF::R_AARCH64_LDST64_ABS_LO12_NC;
       break;
+    case AArch64::LDRSl:
+      AddrReg = Reg;
+      OpCode = AArch64::LDRSui;
+      RelType = ELF::R_AARCH64_LDST32_ABS_LO12_NC;
+      break;
+    case AArch64::LDRDl:
+      AddrReg = Reg;
+      OpCode = AArch64::LDRDui;
+      RelType = ELF::R_AARCH64_LDST64_ABS_LO12_NC;
+      break;
+    case AArch64::LDRQl:
+      AddrReg = Reg;
+      OpCode = AArch64::LDRQui;
+      RelType = ELF::R_AARCH64_LDST128_ABS_LO12_NC;
+      break;
     default:
-      llvm_unreachable("LDR (literal) or LDRSW (literal) expected");
+      llvm_unreachable("LDR (literal), LDRSW (literal), LDR (literal, SIMD&FP) "
+                       "expected");
     }
 
     const MCSymbol *Target = getTargetSymbol(LDRInst, 1);
diff --git a/bolt/test/AArch64/ldr-relaxation.s b/bolt/test/AArch64/ldr-relaxation.s
index 58ffeb034c253..c9eca697ec089 100644
--- a/bolt/test/AArch64/ldr-relaxation.s
+++ b/bolt/test/AArch64/ldr-relaxation.s
@@ -135,6 +135,69 @@ _start:
   ret
   .cfi_endproc
 .size _start, .-_start
+.endif
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \
+# RUN:    --defsym RELAX_SIMPLE_LDR_FP32=1 %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.so -Wl,-q
+# RUN: llvm-bolt %t.so -o %t.bolt -aarch64-relax-ldr-fp-using-la
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s --check-prefix=RELAX_LDR_FP32
+
+# RELAX_LDR_FP32: adrp
+# RELAX_LDR_FP32-NEXT: ldr s0
+.ifdef RELAX_SIMPLE_LDR_FP32
+  .text
+  .global _start
+  .type _start, %function
+_start:
+  .cfi_startproc
+  ldr s0, _bar
+  mov x0, xzr
+  ret
+  .cfi_endproc
+.size _start, .-_start
+.endif
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \
+# RUN:    --defsym RELAX_SIMPLE_LDR_FP64=1 %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.so -Wl,-q
+# RUN: llvm-bolt %t.so -o %t.bolt -aarch64-relax-ldr-fp-using-la
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s --check-prefix=RELAX_LDR_FP64
+
+# RELAX_LDR_FP64: adrp
+# RELAX_LDR_FP64-NEXT: ldr d0
+.ifdef RELAX_SIMPLE_LDR_FP64
+  .text
+  .global _start
+  .type _start, %function
+_start:
+  .cfi_startproc
+  ldr d0, _bar
+  mov x0, xzr
+  ret
+  .cfi_endproc
+.size _start, .-_start
+.endif
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown \
+# RUN:    --defsym RELAX_SIMPLE_LDR_FP128=1 %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.so -Wl,-q
+# RUN: llvm-bolt %t.so -o %t.bolt -aarch64-relax-ldr-fp-using-la
+# RUN: llvm-objdump -d %t.bolt | FileCheck %s --check-prefix=RELAX_LDR_FP128
+
+# RELAX_LDR_FP128: adrp
+# RELAX_LDR_FP128-NEXT: ldr q0
+.ifdef RELAX_SIMPLE_LDR_FP128
+  .text
+  .global _start
+  .type _start, %function
+_start:
+  .cfi_startproc
+  ldr q0, _bar
+  mov x0, xzr
+  ret
+  .cfi_endproc
+.size _start, .-_start
 .endif
 
   .section .text_cold
@@ -143,3 +206,9 @@ _start:
 _foo:
   .long 0x12345678
 .size _foo, .-_foo
+  .global _bar
+  .align 4
+_bar:
+  .xword  0x0000000000000000
+  .xword  0x0000000000000000
+.size _bar, .-_bar



More information about the llvm-commits mailing list