[llvm] [ARM] Use .reloc for weak symbols in PIC mode instead of GOT indirection (PR #208372)

dong jianqiang via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 9 06:11:56 PDT 2026


https://github.com/dongjianqiang2 updated https://github.com/llvm/llvm-project/pull/208372

>From 528684164d0c3c64796eb4e4c7c4ec9864b2595f Mon Sep 17 00:00:00 2001
From: Dong JianQiang <dongjianqiang2 at huawei.com>
Date: Thu, 9 Jul 2026 11:27:14 +0800
Subject: [PATCH 1/3] Revert "[ARM] Use GOT indirection for weak symbols in PIC
 mode  (#198577)"

This reverts commit 31f0e25074627bd081130f81ba6ffcb0ede1aaab.
---
 llvm/lib/Target/ARM/ARMFastISel.cpp      |  3 +--
 llvm/lib/Target/ARM/ARMISelLowering.cpp  | 11 +++--------
 llvm/lib/Target/ARM/ARMSubtarget.cpp     |  3 +--
 llvm/lib/Target/ARM/ARMTargetMachine.h   |  9 ---------
 llvm/test/CodeGen/ARM/elf-preemption.ll  | 14 ++++++--------
 llvm/test/CodeGen/ARM/weak-hidden-pic.ll | 19 -------------------
 6 files changed, 11 insertions(+), 48 deletions(-)
 delete mode 100644 llvm/test/CodeGen/ARM/weak-hidden-pic.ll

diff --git a/llvm/lib/Target/ARM/ARMFastISel.cpp b/llvm/lib/Target/ARM/ARMFastISel.cpp
index 1cf0d98f690aa..2c5d286e11c4f 100644
--- a/llvm/lib/Target/ARM/ARMFastISel.cpp
+++ b/llvm/lib/Target/ARM/ARMFastISel.cpp
@@ -3033,8 +3033,7 @@ bool ARMFastISel::tryToFoldLoadIntoMI(MachineInstr *MI, unsigned OpNo,
 }
 
 Register ARMFastISel::ARMLowerPICELF(const GlobalValue *GV, MVT VT) {
-  // Weak symbols need GOT indirection even when hidden/DSO-local.
-  bool UseGOT_PREL = !GV->isDSOLocal() || GV->isWeakForLinker();
+  bool UseGOT_PREL = !GV->isDSOLocal();
   LLVMContext *Context = &MF->getFunction().getContext();
   unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
   unsigned PCAdj = Subtarget->isThumb() ? 4 : 8;
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 577e97e736c25..26b53b4793fef 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -3651,15 +3651,10 @@ SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op,
       return V;
 
   if (isPositionIndependent()) {
-    // Weak symbols need GOT indirection even when hidden/DSO-local.
-    // The assembler eagerly resolves PC-relative expressions when the
-    // symbol and reference are in the same section, which prevents the
-    // linker from overriding a weak definition with a non-weak one.
-    bool UseGOT = !GV->isDSOLocal() || GV->isWeakForLinker();
-    SDValue G = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0,
-                                           UseGOT ? ARMII::MO_GOT : 0);
+    SDValue G = DAG.getTargetGlobalAddress(
+        GV, dl, PtrVT, 0, GV->isDSOLocal() ? 0 : ARMII::MO_GOT);
     SDValue Result = DAG.getNode(ARMISD::WrapperPIC, dl, PtrVT, G);
-    if (UseGOT)
+    if (!GV->isDSOLocal())
       Result =
           DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Result,
                       MachinePointerInfo::getGOT(DAG.getMachineFunction()));
diff --git a/llvm/lib/Target/ARM/ARMSubtarget.cpp b/llvm/lib/Target/ARM/ARMSubtarget.cpp
index 0f324a6a31757..4893d8d3a9ef1 100644
--- a/llvm/lib/Target/ARM/ARMSubtarget.cpp
+++ b/llvm/lib/Target/ARM/ARMSubtarget.cpp
@@ -397,8 +397,7 @@ bool ARMSubtarget::isGVIndirectSymbol(const GlobalValue *GV) const {
 }
 
 bool ARMSubtarget::isGVInGOT(const GlobalValue *GV) const {
-  return isTargetELF() && TM.isPositionIndependent() &&
-         (!GV->isDSOLocal() || GV->isWeakForLinker());
+  return isTargetELF() && TM.isPositionIndependent() && !GV->isDSOLocal();
 }
 
 unsigned ARMSubtarget::getMispredictionPenalty() const {
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.h b/llvm/lib/Target/ARM/ARMTargetMachine.h
index 109d6fd5e6671..215b541620acb 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.h
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.h
@@ -111,15 +111,6 @@ class ARMBaseTargetMachine : public CodeGenTargetMachineImpl {
         (GV->isDeclarationForLinker() || GV->hasCommonLinkage()))
       return true;
 
-    // In ELF PIC mode, weak symbols referenced via the constant pool use a
-    // PC-relative expression (e.g. .long xxx-(.LPC+8)) that the assembler
-    // eagerly resolves when both the symbol and label are in the same section.
-    // This prevents the linker from overriding a weak definition with a
-    // non-weak one. Use GOT indirection for weak symbols to avoid this.
-    if (getTargetTriple().isOSBinFormatELF() && isPositionIndependent() &&
-        GV->isWeakForLinker())
-      return true;
-
     return false;
   }
 
diff --git a/llvm/test/CodeGen/ARM/elf-preemption.ll b/llvm/test/CodeGen/ARM/elf-preemption.ll
index 88b35683b97c0..154c29c1c029c 100644
--- a/llvm/test/CodeGen/ARM/elf-preemption.ll
+++ b/llvm/test/CodeGen/ARM/elf-preemption.ll
@@ -59,13 +59,12 @@ define ptr @get_weak_dsolocal_var() nounwind {
 ; PIC:       @ %bb.0:
 ; PIC-NEXT:    ldr r0, .LCPI2_0
 ; PIC-NEXT:  .LPC2_0:
-; PIC-NEXT:    ldr r0, [pc, r0]
+; PIC-NEXT:    add r0, pc, r0
 ; PIC-NEXT:    bx lr
 ; PIC-NEXT:    .p2align 2
 ; PIC-NEXT:  @ %bb.1:
 ; PIC-NEXT:  .LCPI2_0:
-; PIC-NEXT:  .Ltmp1:
-; PIC-NEXT:    .long weak_dsolocal_var(GOT_PREL)-(.LPC2_0+8-.Ltmp1)
+; PIC-NEXT:    .long weak_dsolocal_var-(.LPC2_0+8)
   ret ptr @weak_dsolocal_var
 }
 
@@ -127,8 +126,8 @@ define dso_preemptable ptr @preemptable_func() nounwind {
 ; PIC-NEXT:    .p2align 2
 ; PIC-NEXT:  @ %bb.1:
 ; PIC-NEXT:  .LCPI5_0:
-; PIC-NEXT:  .Ltmp2:
-; PIC-NEXT:    .long preemptable_func(GOT_PREL)-(.LPC5_0+8-.Ltmp2)
+; PIC-NEXT:  .Ltmp1:
+; PIC-NEXT:    .long preemptable_func(GOT_PREL)-(.LPC5_0+8-.Ltmp1)
   ret ptr @preemptable_func
 }
 
@@ -163,13 +162,12 @@ define weak dso_local ptr @weak_dsolocal_func() nounwind {
 ; PIC:       @ %bb.0:
 ; PIC-NEXT:    ldr r0, .LCPI7_0
 ; PIC-NEXT:  .LPC7_0:
-; PIC-NEXT:    ldr r0, [pc, r0]
+; PIC-NEXT:    add r0, pc, r0
 ; PIC-NEXT:    bx lr
 ; PIC-NEXT:    .p2align 2
 ; PIC-NEXT:  @ %bb.1:
 ; PIC-NEXT:  .LCPI7_0:
-; PIC-NEXT:  .Ltmp3:
-; PIC-NEXT:    .long weak_dsolocal_func(GOT_PREL)-(.LPC7_0+8-.Ltmp3)
+; PIC-NEXT:    .long weak_dsolocal_func-(.LPC7_0+8)
   ret ptr @weak_dsolocal_func
 }
 
diff --git a/llvm/test/CodeGen/ARM/weak-hidden-pic.ll b/llvm/test/CodeGen/ARM/weak-hidden-pic.ll
deleted file mode 100644
index 4d6eca77af855..0000000000000
--- a/llvm/test/CodeGen/ARM/weak-hidden-pic.ll
+++ /dev/null
@@ -1,19 +0,0 @@
-; RUN: llc < %s -mtriple=armv7-linux-gnueabi -relocation-model=pic | FileCheck %s
-; RUN: llc < %s -mtriple=thumbv7-linux-gnueabi -relocation-model=pic | FileCheck %s
-; RUN: llc < %s -O0 -fast-isel-abort=2 -mtriple=armv7-linux-gnueabi -relocation-model=pic | FileCheck %s
-
-; Hidden weak function with dso_local must still use GOT indirection
-; in PIC mode on ARM. The assembler eagerly resolves PC-relative
-; expressions like .long xxx-(.LPC+8) when both are in the same section,
-; which prevents the linker from overriding the weak definition with
-; a non-weak one from another object file.
-
-define weak dso_local hidden void @weak_hidden_func() {
-  ret void
-}
-
-; CHECK-LABEL: weak_hidden_func_addr:
-; CHECK:       .long weak_hidden_func(GOT_PREL)
-define i8* @weak_hidden_func_addr() {
-  ret i8* bitcast (void()* @weak_hidden_func to i8*)
-}

>From 0563c7d23e4892bcbeec46d6a3d9bd3ae00a5627 Mon Sep 17 00:00:00 2001
From: Dong JianQiang <dongjianqiang2 at huawei.com>
Date: Thu, 9 Jul 2026 11:28:01 +0800
Subject: [PATCH 2/3] [ARM] Use .reloc for weak symbols in PIC mode instead of
 GOT indirection

Instead of forcing weak symbols through the GOT (which requires an extra
memory load), use a PC-relative constant pool entry with a .reloc
directive to force the assembler to emit a proper R_ARM_REL32 relocation.
This prevents the assembler from eagerly resolving the PC-relative
expression when the symbol and reference are in the same section, while
preserving the more efficient single-load code sequence.

The .reloc is emitted only for weak symbols in ELF PIC mode, where the
constant pool expression would otherwise be eagerly resolved by the
assembler.

Fixes #183916
---
 llvm/lib/Target/ARM/ARMAsmPrinter.cpp    | 29 +++++++++++++++++++++++
 llvm/test/CodeGen/ARM/elf-preemption.ll  | 30 +++++++-----------------
 llvm/test/CodeGen/ARM/weak-hidden-pic.ll | 20 ++++++++++++++++
 3 files changed, 57 insertions(+), 22 deletions(-)
 create mode 100644 llvm/test/CodeGen/ARM/weak-hidden-pic.ll

diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index c25a2cfeff8d6..9a00d5ff30913 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -1016,6 +1016,35 @@ void ARMAsmPrinter::emitMachineConstantPoolValue(
     unsigned char TF =
         TM.getTargetTriple().isOSBinFormatMachO() ? ARMII::MO_NONLAZY : 0;
     MCSym = GetARMGVSymbol(GV, TF);
+
+    // For weak symbols in ELF PIC mode, the assembler would eagerly resolve a
+    // PC-relative expression like sym-(LPC+8) when the symbol and reference are
+    // in the same section, preventing the linker from overriding a weak
+    // definition with a strong one. Use a .reloc directive to force a proper
+    // relocation (R_ARM_REL32) so the linker can perform the override.
+    if (GV->isWeakForLinker() && TM.getTargetTriple().isOSBinFormatELF() &&
+        TM.isPositionIndependent() && ACPV->getPCAdjustment() != 0) {
+      MCSymbol *CPILabel = OutContext.createTempSymbol();
+      OutStreamer->emitLabel(CPILabel);
+      // Emit local-only expression: CPILabel - (LPC+PCAdj)
+      const MCExpr *LocalExpr = MCSymbolRefExpr::create(CPILabel, OutContext);
+      MCSymbol *PCLabel =
+          getPICLabel(DL.getInternalSymbolPrefix(), getFunctionNumber(),
+                      ACPV->getLabelId(), OutContext);
+      const MCExpr *PCRelExpr = MCSymbolRefExpr::create(PCLabel, OutContext);
+      PCRelExpr = MCBinaryExpr::createAdd(
+          PCRelExpr,
+          MCConstantExpr::create(ACPV->getPCAdjustment(), OutContext),
+          OutContext);
+      LocalExpr = MCBinaryExpr::createSub(LocalExpr, PCRelExpr, OutContext);
+      OutStreamer->emitValue(LocalExpr, Size);
+      // Emit .reloc to force linker resolution of the weak symbol.
+      const MCExpr *CPIExpr = MCSymbolRefExpr::create(CPILabel, OutContext);
+      const MCExpr *SymExpr = MCSymbolRefExpr::create(MCSym, OutContext);
+      OutStreamer->emitRelocDirective(*CPIExpr, "R_ARM_REL32", SymExpr,
+                                      SMLoc());
+      return;
+    }
   } else if (ACPV->isMachineBasicBlock()) {
     const MachineBasicBlock *MBB = cast<ARMConstantPoolMBB>(ACPV)->getMBB();
     MCSym = MBB->getSymbol();
diff --git a/llvm/test/CodeGen/ARM/elf-preemption.ll b/llvm/test/CodeGen/ARM/elf-preemption.ll
index 154c29c1c029c..05552c6d4282b 100644
--- a/llvm/test/CodeGen/ARM/elf-preemption.ll
+++ b/llvm/test/CodeGen/ARM/elf-preemption.ll
@@ -64,7 +64,9 @@ define ptr @get_weak_dsolocal_var() nounwind {
 ; PIC-NEXT:    .p2align 2
 ; PIC-NEXT:  @ %bb.1:
 ; PIC-NEXT:  .LCPI2_0:
-; PIC-NEXT:    .long weak_dsolocal_var-(.LPC2_0+8)
+; PIC-NEXT:  .Ltmp1:
+; PIC-NEXT:    .long .Ltmp1-(.LPC2_0+8)
+; PIC-NEXT:    .reloc .Ltmp1, R_ARM_REL32, weak_dsolocal_var
   ret ptr @weak_dsolocal_var
 }
 
@@ -126,8 +128,8 @@ define dso_preemptable ptr @preemptable_func() nounwind {
 ; PIC-NEXT:    .p2align 2
 ; PIC-NEXT:  @ %bb.1:
 ; PIC-NEXT:  .LCPI5_0:
-; PIC-NEXT:  .Ltmp1:
-; PIC-NEXT:    .long preemptable_func(GOT_PREL)-(.LPC5_0+8-.Ltmp1)
+; PIC-NEXT:  .Ltmp2:
+; PIC-NEXT:    .long preemptable_func(GOT_PREL)-(.LPC5_0+8-.Ltmp2)
   ret ptr @preemptable_func
 }
 
@@ -137,17 +139,6 @@ define dso_local ptr @dsolocal_func() nounwind {
 ; STATIC-NEXT:    movw r0, :lower16:dsolocal_func
 ; STATIC-NEXT:    movt r0, :upper16:dsolocal_func
 ; STATIC-NEXT:    bx lr
-;
-; PIC-LABEL: dsolocal_func:
-; PIC:       @ %bb.0:
-; PIC-NEXT:    ldr r0, .LCPI6_0
-; PIC-NEXT:  .LPC6_0:
-; PIC-NEXT:    add r0, pc, r0
-; PIC-NEXT:    bx lr
-; PIC-NEXT:    .p2align 2
-; PIC-NEXT:  @ %bb.1:
-; PIC-NEXT:  .LCPI6_0:
-; PIC-NEXT:    .long .Ldsolocal_func$local-(.LPC6_0+8)
   ret ptr @dsolocal_func
 }
 
@@ -167,7 +158,9 @@ define weak dso_local ptr @weak_dsolocal_func() nounwind {
 ; PIC-NEXT:    .p2align 2
 ; PIC-NEXT:  @ %bb.1:
 ; PIC-NEXT:  .LCPI7_0:
-; PIC-NEXT:    .long weak_dsolocal_func-(.LPC7_0+8)
+; PIC-NEXT:  .Ltmp3:
+; PIC-NEXT:    .long .Ltmp3-(.LPC7_0+8)
+; PIC-NEXT:    .reloc .Ltmp3, R_ARM_REL32, weak_dsolocal_func
   ret ptr @weak_dsolocal_func
 }
 
@@ -180,13 +173,6 @@ define dso_local void @call_dsolocal_func() nounwind {
 ; STATIC-NEXT:    push {r11, lr}
 ; STATIC-NEXT:    bl dsolocal_func
 ; STATIC-NEXT:    pop {r11, pc}
-;
-; PIC-LABEL: call_dsolocal_func:
-; PIC:       @ %bb.0:
-; PIC-NEXT:    .save {r11, lr}
-; PIC-NEXT:    push {r11, lr}
-; PIC-NEXT:    bl .Ldsolocal_func$local
-; PIC-NEXT:    pop {r11, pc}
   call ptr @dsolocal_func()
   ret void
 }
diff --git a/llvm/test/CodeGen/ARM/weak-hidden-pic.ll b/llvm/test/CodeGen/ARM/weak-hidden-pic.ll
new file mode 100644
index 0000000000000..9ff1675db1944
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/weak-hidden-pic.ll
@@ -0,0 +1,20 @@
+; RUN: llc < %s -mtriple=armv7-linux-gnueabi -relocation-model=pic | FileCheck %s
+; RUN: llc < %s -mtriple=thumbv7-linux-gnueabi -relocation-model=pic | FileCheck %s
+; RUN: llc < %s -O0 -fast-isel-abort=2 -mtriple=armv7-linux-gnueabi -relocation-model=pic | FileCheck %s
+
+; Weak dso_local hidden functions must be overridable at link time
+; (a strong definition in another object should override the weak one).
+; Instead of using GOT indirection, we use a PC-relative constant pool
+; entry with a .reloc directive to force the assembler to emit a proper
+; relocation (R_ARM_REL32), preventing eager resolution when the symbol
+; and reference are in the same section.
+
+define weak dso_local hidden void @weak_hidden_func() {
+  ret void
+}
+
+; CHECK-LABEL: weak_hidden_func_addr:
+; CHECK:       .reloc .Ltmp{{[0-9]+}}, R_ARM_REL32, weak_hidden_func
+define i8* @weak_hidden_func_addr() {
+  ret i8* bitcast (void()* @weak_hidden_func to i8*)
+}

>From d4782ee048cd2e03c135851adeb0fcfebdae2f74 Mon Sep 17 00:00:00 2001
From: Dong JianQiang <dongjianqiang2 at huawei.com>
Date: Thu, 9 Jul 2026 21:09:05 +0800
Subject: [PATCH 3/3] [ARM] Address review feedback on weak-symbol .reloc PIC
 handling

- Reword the constant-pool .reloc comment to describe the override as a
  "non-weak definition from another section" and the mechanism as forcing
  a relocation "rather than a fixup".
- Restore the PIC-LABEL checks for dsolocal_func and call_dsolocal_func
  in elf-preemption.ll. update_llc_test_checks.py silently drops these for
  functions that emit a .L<name>$local alias, even though their PIC
  codegen is unchanged.
- Use "non-weak definition" consistently in weak-hidden-pic.ll.
---
 llvm/lib/Target/ARM/ARMAsmPrinter.cpp    |  3 ++-
 llvm/test/CodeGen/ARM/elf-preemption.ll  | 18 ++++++++++++++++++
 llvm/test/CodeGen/ARM/weak-hidden-pic.ll |  2 +-
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
index 9a00d5ff30913..b7448dd5c9783 100644
--- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
+++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp
@@ -1020,7 +1020,8 @@ void ARMAsmPrinter::emitMachineConstantPoolValue(
     // For weak symbols in ELF PIC mode, the assembler would eagerly resolve a
     // PC-relative expression like sym-(LPC+8) when the symbol and reference are
     // in the same section, preventing the linker from overriding a weak
-    // definition with a strong one. Use a .reloc directive to force a proper
+    // definition with a non-weak definition from another section. Use a
+    // .reloc directive rather than a fixup to force the generation of a
     // relocation (R_ARM_REL32) so the linker can perform the override.
     if (GV->isWeakForLinker() && TM.getTargetTriple().isOSBinFormatELF() &&
         TM.isPositionIndependent() && ACPV->getPCAdjustment() != 0) {
diff --git a/llvm/test/CodeGen/ARM/elf-preemption.ll b/llvm/test/CodeGen/ARM/elf-preemption.ll
index 05552c6d4282b..6d5a78dd95aa4 100644
--- a/llvm/test/CodeGen/ARM/elf-preemption.ll
+++ b/llvm/test/CodeGen/ARM/elf-preemption.ll
@@ -139,6 +139,17 @@ define dso_local ptr @dsolocal_func() nounwind {
 ; STATIC-NEXT:    movw r0, :lower16:dsolocal_func
 ; STATIC-NEXT:    movt r0, :upper16:dsolocal_func
 ; STATIC-NEXT:    bx lr
+;
+; PIC-LABEL: dsolocal_func:
+; PIC:       @ %bb.0:
+; PIC-NEXT:    ldr r0, .LCPI6_0
+; PIC-NEXT:  .LPC6_0:
+; PIC-NEXT:    add r0, pc, r0
+; PIC-NEXT:    bx lr
+; PIC-NEXT:    .p2align 2
+; PIC-NEXT:  @ %bb.1:
+; PIC-NEXT:  .LCPI6_0:
+; PIC-NEXT:    .long .Ldsolocal_func$local-(.LPC6_0+8)
   ret ptr @dsolocal_func
 }
 
@@ -173,6 +184,13 @@ define dso_local void @call_dsolocal_func() nounwind {
 ; STATIC-NEXT:    push {r11, lr}
 ; STATIC-NEXT:    bl dsolocal_func
 ; STATIC-NEXT:    pop {r11, pc}
+;
+; PIC-LABEL: call_dsolocal_func:
+; PIC:       @ %bb.0:
+; PIC-NEXT:    .save {r11, lr}
+; PIC-NEXT:    push {r11, lr}
+; PIC-NEXT:    bl .Ldsolocal_func$local
+; PIC-NEXT:    pop {r11, pc}
   call ptr @dsolocal_func()
   ret void
 }
diff --git a/llvm/test/CodeGen/ARM/weak-hidden-pic.ll b/llvm/test/CodeGen/ARM/weak-hidden-pic.ll
index 9ff1675db1944..b446d52e849d3 100644
--- a/llvm/test/CodeGen/ARM/weak-hidden-pic.ll
+++ b/llvm/test/CodeGen/ARM/weak-hidden-pic.ll
@@ -3,7 +3,7 @@
 ; RUN: llc < %s -O0 -fast-isel-abort=2 -mtriple=armv7-linux-gnueabi -relocation-model=pic | FileCheck %s
 
 ; Weak dso_local hidden functions must be overridable at link time
-; (a strong definition in another object should override the weak one).
+; (a non-weak definition in another object should override the weak one).
 ; Instead of using GOT indirection, we use a PC-relative constant pool
 ; entry with a .reloc directive to force the assembler to emit a proper
 ; relocation (R_ARM_REL32), preventing eager resolution when the symbol



More information about the llvm-commits mailing list