https://github.com/jxz-hw updated 
https://github.com/llvm/llvm-project/pull/147313

>From 74da236e8ea109543cd423d1128c8dfb30ef4a0a Mon Sep 17 00:00:00 2001
From: jxz-hw <[email protected]>
Date: Sun, 7 Jun 2026 20:27:36 +0800
Subject: [PATCH 1/2] [ARM] Support -mlong-calls with -fPIC on ARM ELF targets

fix issue #39970 and #142982

- GlobalAddress: use GOT-based addressing via WrapperPIC.  DSO-local symbols 
use a plain PC-relative WrapperPIC; non-DSO-local symbols additionally load the 
target address from the GOT.
- ExternalSymbol: load the symbol address from the GOT via a GOT_PREL constant 
pool entry followed by a PICLDR.
- With -mexecute-only: movw/movt pairs are used instead of constant pools. 
Note: execute-only may not support PIC; this path is only reached for non-PIC 
targets.
---
 llvm/lib/Target/ARM/ARMConstantPoolValue.cpp  |   9 +-
 llvm/lib/Target/ARM/ARMConstantPoolValue.h    |   5 +-
 llvm/lib/Target/ARM/ARMISelLowering.cpp       |  50 +++-
 .../ARM/subtarget-features-long-calls.ll      | 261 ++++++++++++++++--
 4 files changed, 287 insertions(+), 38 deletions(-)

diff --git a/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp 
b/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp
index 2a3f33fc44b59..1c1d66c83bb73 100644
--- a/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp
+++ b/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp
@@ -230,10 +230,11 @@ ARMConstantPoolSymbol::ARMConstantPoolSymbol(LLVMContext 
&C, StringRef s,
                            AddCurrentAddress),
       S(std::string(s)) {}
 
-ARMConstantPoolSymbol *ARMConstantPoolSymbol::Create(LLVMContext &C,
-                                                     StringRef s, unsigned ID,
-                                                     unsigned char PCAdj) {
-  return new ARMConstantPoolSymbol(C, s, ID, PCAdj, ARMCP::no_modifier, false);
+ARMConstantPoolSymbol *
+ARMConstantPoolSymbol::Create(LLVMContext &C, StringRef s, unsigned ID,
+                              unsigned char PCAdj,
+                              ARMCP::ARMCPModifier Modifier) {
+  return new ARMConstantPoolSymbol(C, s, ID, PCAdj, Modifier, false);
 }
 
 int ARMConstantPoolSymbol::getExistingMachineCPValue(MachineConstantPool *CP,
diff --git a/llvm/lib/Target/ARM/ARMConstantPoolValue.h 
b/llvm/lib/Target/ARM/ARMConstantPoolValue.h
index e21b2c935c010..2c24b6551c778 100644
--- a/llvm/lib/Target/ARM/ARMConstantPoolValue.h
+++ b/llvm/lib/Target/ARM/ARMConstantPoolValue.h
@@ -213,8 +213,9 @@ class ARMConstantPoolSymbol : public ARMConstantPoolValue {
                         bool AddCurrentAddress);
 
 public:
-  static ARMConstantPoolSymbol *Create(LLVMContext &C, StringRef s, unsigned 
ID,
-                                       unsigned char PCAdj);
+  static ARMConstantPoolSymbol *
+  Create(LLVMContext &C, StringRef s, unsigned ID, unsigned char PCAdj,
+         ARMCP::ARMCPModifier Modifier = ARMCP::no_modifier);
 
   StringRef getSymbol() const { return S; }
 
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp 
b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index e08a4f5ebda08..361544ca1396e 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -2438,19 +2438,40 @@ 
ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
   auto PtrVt = getPointerTy(DAG.getDataLayout());
 
   if (Subtarget->genLongCalls()) {
-    assert((!isPositionIndependent() || TT.isOSWindows()) &&
-           "long-calls codegen is not position independent!");
+    bool isPIC = isPositionIndependent() && !TT.isOSWindows();
+    if (isPIC && Subtarget->genExecuteOnly())
+      report_fatal_error(Twine("long-calls with execute-only and "
+                               "position-independent code is not supported"),
+                         /*gen_crash_diag=*/false);
+    if (Subtarget->isROPI())
+      report_fatal_error(
+          Twine("long-calls with ROPI is not currently supported"),
+          /*gen_crash_diag=*/false);
+
     // Handle a global address or an external symbol. If it's not one of
     // those, the target's already in a register, so we don't need to do
     // anything extra.
     if (isa<GlobalAddressSDNode>(Callee)) {
       if (Subtarget->genExecuteOnly()) {
+        // Execute-only forbids constant pools in .text, so use movw/movt.
+        // fPIC is not supported with execute-only.
         if (Subtarget->useMovt())
           ++NumMovwMovt;
         Callee = DAG.getNode(ARMISD::Wrapper, dl, PtrVt,
                              DAG.getTargetGlobalAddress(GVal, dl, PtrVt));
+      } else if (isPIC) {
+        // PIC without execute-only: use GOT-based addressing.
+        // DSO-local symbols use a plain PC-relative WrapperPIC;
+        // non-DSO-local symbols additionally load the address from the GOT.
+        SDValue G = DAG.getTargetGlobalAddress(
+            GVal, dl, PtrVt, 0, GVal->isDSOLocal() ? 0 : ARMII::MO_GOT);
+        Callee = DAG.getNode(ARMISD::WrapperPIC, dl, PtrVt, G);
+        if (!GVal->isDSOLocal())
+          Callee =
+              DAG.getLoad(PtrVt, dl, DAG.getEntryNode(), Callee,
+                          
MachinePointerInfo::getGOT(DAG.getMachineFunction()));
       } else {
-        // Create a constant pool entry for the callee address
+        // Neither execute-only nor PIC: load the address from a constant pool.
         unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
         ARMConstantPoolValue *CPV = ARMConstantPoolConstant::Create(
             GVal, ARMPCLabelIndex, ARMCP::CPValue, 0);
@@ -2466,12 +2487,31 @@ 
ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
       const char *Sym = S->getSymbol();
 
       if (Subtarget->genExecuteOnly()) {
+        // Execute-only forbids constant pools in .text, so use movw/movt.
+        // fPIC is not supported with execute-only.
         if (Subtarget->useMovt())
           ++NumMovwMovt;
         Callee = DAG.getNode(ARMISD::Wrapper, dl, PtrVt,
-                             DAG.getTargetGlobalAddress(GVal, dl, PtrVt));
+                             DAG.getTargetExternalSymbol(Sym, PtrVt, 0));
+      } else if (isPIC) {
+        // PIC without execute-only: use GOT-based addressing via a GOT_PREL
+        // constant pool entry.
+        unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
+        ARMConstantPoolValue *CPV = ARMConstantPoolSymbol::Create(
+            *DAG.getContext(), Sym, ARMPCLabelIndex, /*PCAdj=*/0,
+            ARMCP::GOT_PREL);
+        SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVt, Align(4));
+        CPAddr = DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr);
+        SDValue GOTOffset = DAG.getLoad(
+            PtrVt, dl, DAG.getEntryNode(), CPAddr,
+            MachinePointerInfo::getConstantPool(DAG.getMachineFunction()));
+        SDValue PICLabel = DAG.getConstant(ARMPCLabelIndex, dl, MVT::i32);
+        Callee = DAG.getNode(ARMISD::PIC_ADD, dl, PtrVt, GOTOffset, PICLabel);
+        Callee =
+            DAG.getLoad(PtrVt, dl, DAG.getEntryNode(), Callee,
+                        MachinePointerInfo::getGOT(DAG.getMachineFunction()));
       } else {
-        // Create a constant pool entry for the callee address
+        // Neither execute-only nor PIC: load the address from a constant pool.
         unsigned ARMPCLabelIndex = AFI->createPICLabelUId();
         ARMConstantPoolValue *CPV = ARMConstantPoolSymbol::Create(
             *DAG.getContext(), Sym, ARMPCLabelIndex, 0);
diff --git a/llvm/test/CodeGen/ARM/subtarget-features-long-calls.ll 
b/llvm/test/CodeGen/ARM/subtarget-features-long-calls.ll
index 112a840ba92ee..b8e0884acd67a 100644
--- a/llvm/test/CodeGen/ARM/subtarget-features-long-calls.ll
+++ b/llvm/test/CodeGen/ARM/subtarget-features-long-calls.ll
@@ -1,44 +1,127 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 
UTC_ARGS: --version 6
 ; RUN: llc -mtriple=thumb-- -mcpu=cortex-a8 -relocation-model=static %s -o - | 
FileCheck -check-prefix=NO-OPTION %s
 ; RUN: llc -mtriple=thumb-- -mcpu=cortex-a8 -relocation-model=static %s -o - 
-mattr=+long-calls | FileCheck -check-prefix=LONGCALL %s
 ; RUN: llc -mtriple=thumb-- -mcpu=cortex-a8 -relocation-model=static %s -o - 
-mattr=-long-calls | FileCheck -check-prefix=NO-LONGCALL %s
 ; RUN: llc -mtriple=thumb-- -mcpu=cortex-a8 -relocation-model=static %s -o - 
-O0 | FileCheck -check-prefix=NO-OPTION %s
 ; RUN: llc -mtriple=thumb-- -mcpu=cortex-a8 -relocation-model=static %s -o - 
-O0 -mattr=+long-calls | FileCheck -check-prefix=LONGCALL %s
 ; RUN: llc -mtriple=thumb-- -mcpu=cortex-a8 -relocation-model=static %s -o - 
-O0 -mattr=-long-calls | FileCheck -check-prefix=NO-LONGCALL %s
-
-; NO-OPTION-LABEL: {{_?}}caller0
-; NO-OPTION: ldr [[R0:r[0-9]+]], [[L0:.*]] 
-; NO-OPTION: blx [[R0]]
-; NO-OPTION: [[L0]]:
-; NO-OPTION: .long {{_?}}callee0
-
-; LONGCALL-LABEL: {{_?}}caller0
-; LONGCALL: ldr [[R0:r[0-9]+]], [[L0:.*]]
-; LONGCALL: blx [[R0]]
-; LONGCALL: [[L0]]:
-; LONGCALL: .long {{_?}}callee0
-
-; NO-LONGCALL-LABEL: {{_?}}caller0
-; NO-LONGCALL: bl {{_?}}callee0
+; RUN: llc -mtriple=arm-linux-gnueabi -mcpu=cortex-a8 -relocation-model=static 
%s -o - -mattr=+long-calls,+execute-only | FileCheck -check-prefix=XO-LONGCALL 
%s
+; RUN: llc -mtriple=arm-linux-gnueabi -mcpu=cortex-a8 -relocation-model=pic %s 
-o - -mattr=+long-calls | FileCheck -check-prefix=PIC-LONGCALL %s
 
 define i32 @caller0() #0 {
+; NO-OPTION-LABEL: caller0:
+; NO-OPTION:       @ %bb.0: @ %entry
+; NO-OPTION-NEXT:    push {r7, lr}
+; NO-OPTION-NEXT:    ldr r0, .LCPI0_0
+; NO-OPTION-NEXT:    blx r0
+; NO-OPTION-NEXT:    movs r0, #0
+; NO-OPTION-NEXT:    pop {r7, pc}
+; NO-OPTION-NEXT:    .p2align 2
+; NO-OPTION-NEXT:  @ %bb.1:
+; NO-OPTION-NEXT:  .LCPI0_0:
+; NO-OPTION-NEXT:    .long callee0
+;
+; LONGCALL-LABEL: caller0:
+; LONGCALL:       @ %bb.0: @ %entry
+; LONGCALL-NEXT:    push {r7, lr}
+; LONGCALL-NEXT:    ldr r0, .LCPI0_0
+; LONGCALL-NEXT:    blx r0
+; LONGCALL-NEXT:    movs r0, #0
+; LONGCALL-NEXT:    pop {r7, pc}
+; LONGCALL-NEXT:    .p2align 2
+; LONGCALL-NEXT:  @ %bb.1:
+; LONGCALL-NEXT:  .LCPI0_0:
+; LONGCALL-NEXT:    .long callee0
+;
+; NO-LONGCALL-LABEL: caller0:
+; NO-LONGCALL:       @ %bb.0: @ %entry
+; NO-LONGCALL-NEXT:    push {r7, lr}
+; NO-LONGCALL-NEXT:    bl callee0
+; NO-LONGCALL-NEXT:    movs r0, #0
+; NO-LONGCALL-NEXT:    pop {r7, pc}
+;
+; XO-LONGCALL-LABEL: caller0:
+; XO-LONGCALL:       @ %bb.0: @ %entry
+; XO-LONGCALL-NEXT:    .save {r11, lr}
+; XO-LONGCALL-NEXT:    push {r11, lr}
+; XO-LONGCALL-NEXT:    movw r0, :lower16:callee0
+; XO-LONGCALL-NEXT:    movt r0, :upper16:callee0
+; XO-LONGCALL-NEXT:    blx r0
+; XO-LONGCALL-NEXT:    mov r0, #0
+; XO-LONGCALL-NEXT:    pop {r11, pc}
+;
+; PIC-LONGCALL-LABEL: caller0:
+; PIC-LONGCALL:       @ %bb.0: @ %entry
+; PIC-LONGCALL-NEXT:    .save {r11, lr}
+; PIC-LONGCALL-NEXT:    push {r11, lr}
+; PIC-LONGCALL-NEXT:    ldr r0, .LCPI0_0
+; PIC-LONGCALL-NEXT:  .LPC0_0:
+; PIC-LONGCALL-NEXT:    ldr r0, [pc, r0]
+; PIC-LONGCALL-NEXT:    blx r0
+; PIC-LONGCALL-NEXT:    mov r0, #0
+; PIC-LONGCALL-NEXT:    pop {r11, pc}
+; PIC-LONGCALL-NEXT:    .p2align 2
+; PIC-LONGCALL-NEXT:  @ %bb.1:
+; PIC-LONGCALL-NEXT:  .LCPI0_0:
+; PIC-LONGCALL-NEXT:  .Ltmp0:
+; PIC-LONGCALL-NEXT:    .long callee0(GOT_PREL)-(.LPC0_0+8-.Ltmp0)
 entry:
   tail call void @callee0()
   ret i32 0
 }
 
-; NO-OPTION-LABEL: {{_?}}caller1
-; NO-OPTION: bl {{_?}}callee0
-
-; LONGCALL-LABEL: {{_?}}caller1
-; LONGCALL: ldr [[R0:r[0-9]+]], [[L0:.*]]
-; LONGCALL: blx [[R0]]
-; LONGCALL: [[L0]]:
-; LONGCALL: .long {{_?}}callee0
-
-; NO-LONGCALL-LABEL: {{_?}}caller1
-; NO-LONGCALL: bl {{_?}}callee0
-
 define i32 @caller1() {
+; NO-OPTION-LABEL: caller1:
+; NO-OPTION:       @ %bb.0: @ %entry
+; NO-OPTION-NEXT:    push {r7, lr}
+; NO-OPTION-NEXT:    bl callee0
+; NO-OPTION-NEXT:    movs r0, #0
+; NO-OPTION-NEXT:    pop {r7, pc}
+;
+; LONGCALL-LABEL: caller1:
+; LONGCALL:       @ %bb.0: @ %entry
+; LONGCALL-NEXT:    push {r7, lr}
+; LONGCALL-NEXT:    ldr r0, .LCPI1_0
+; LONGCALL-NEXT:    blx r0
+; LONGCALL-NEXT:    movs r0, #0
+; LONGCALL-NEXT:    pop {r7, pc}
+; LONGCALL-NEXT:    .p2align 2
+; LONGCALL-NEXT:  @ %bb.1:
+; LONGCALL-NEXT:  .LCPI1_0:
+; LONGCALL-NEXT:    .long callee0
+;
+; NO-LONGCALL-LABEL: caller1:
+; NO-LONGCALL:       @ %bb.0: @ %entry
+; NO-LONGCALL-NEXT:    push {r7, lr}
+; NO-LONGCALL-NEXT:    bl callee0
+; NO-LONGCALL-NEXT:    movs r0, #0
+; NO-LONGCALL-NEXT:    pop {r7, pc}
+;
+; XO-LONGCALL-LABEL: caller1:
+; XO-LONGCALL:       @ %bb.0: @ %entry
+; XO-LONGCALL-NEXT:    .save {r11, lr}
+; XO-LONGCALL-NEXT:    push {r11, lr}
+; XO-LONGCALL-NEXT:    movw r0, :lower16:callee0
+; XO-LONGCALL-NEXT:    movt r0, :upper16:callee0
+; XO-LONGCALL-NEXT:    blx r0
+; XO-LONGCALL-NEXT:    mov r0, #0
+; XO-LONGCALL-NEXT:    pop {r11, pc}
+;
+; PIC-LONGCALL-LABEL: caller1:
+; PIC-LONGCALL:       @ %bb.0: @ %entry
+; PIC-LONGCALL-NEXT:    .save {r11, lr}
+; PIC-LONGCALL-NEXT:    push {r11, lr}
+; PIC-LONGCALL-NEXT:    ldr r0, .LCPI1_0
+; PIC-LONGCALL-NEXT:  .LPC1_0:
+; PIC-LONGCALL-NEXT:    ldr r0, [pc, r0]
+; PIC-LONGCALL-NEXT:    blx r0
+; PIC-LONGCALL-NEXT:    mov r0, #0
+; PIC-LONGCALL-NEXT:    pop {r11, pc}
+; PIC-LONGCALL-NEXT:    .p2align 2
+; PIC-LONGCALL-NEXT:  @ %bb.1:
+; PIC-LONGCALL-NEXT:  .LCPI1_0:
+; PIC-LONGCALL-NEXT:  .Ltmp1:
+; PIC-LONGCALL-NEXT:    .long callee0(GOT_PREL)-(.LPC1_0+8-.Ltmp1)
 entry:
   tail call void @callee0()
   ret i32 0
@@ -47,3 +130,127 @@ entry:
 declare void @callee0()
 
 attributes #0 = { "target-features"="+long-calls" }
+
+define dso_local void @global_func() {
+; NO-OPTION-LABEL: global_func:
+; NO-OPTION:       @ %bb.0: @ %entry
+; NO-OPTION-NEXT:    bx lr
+;
+; LONGCALL-LABEL: global_func:
+; LONGCALL:       @ %bb.0: @ %entry
+; LONGCALL-NEXT:    bx lr
+;
+; NO-LONGCALL-LABEL: global_func:
+; NO-LONGCALL:       @ %bb.0: @ %entry
+; NO-LONGCALL-NEXT:    bx lr
+;
+; XO-LONGCALL-LABEL: global_func:
+; XO-LONGCALL:       @ %bb.0: @ %entry
+; XO-LONGCALL-NEXT:    bx lr
+entry:
+  ret void
+}
+
+define void @test_global() {
+; NO-OPTION-LABEL: test_global:
+; NO-OPTION:       @ %bb.0: @ %entry
+; NO-OPTION-NEXT:    push {r7, lr}
+; NO-OPTION-NEXT:    bl global_func
+; NO-OPTION-NEXT:    pop {r7, pc}
+;
+; LONGCALL-LABEL: test_global:
+; LONGCALL:       @ %bb.0: @ %entry
+; LONGCALL-NEXT:    push {r7, lr}
+; LONGCALL-NEXT:    ldr r0, .LCPI3_0
+; LONGCALL-NEXT:    blx r0
+; LONGCALL-NEXT:    pop {r7, pc}
+; LONGCALL-NEXT:    .p2align 2
+; LONGCALL-NEXT:  @ %bb.1:
+; LONGCALL-NEXT:  .LCPI3_0:
+; LONGCALL-NEXT:    .long global_func
+;
+; NO-LONGCALL-LABEL: test_global:
+; NO-LONGCALL:       @ %bb.0: @ %entry
+; NO-LONGCALL-NEXT:    push {r7, lr}
+; NO-LONGCALL-NEXT:    bl global_func
+; NO-LONGCALL-NEXT:    pop {r7, pc}
+;
+; XO-LONGCALL-LABEL: test_global:
+; XO-LONGCALL:       @ %bb.0: @ %entry
+; XO-LONGCALL-NEXT:    .save {r11, lr}
+; XO-LONGCALL-NEXT:    push {r11, lr}
+; XO-LONGCALL-NEXT:    movw r0, :lower16:global_func
+; XO-LONGCALL-NEXT:    movt r0, :upper16:global_func
+; XO-LONGCALL-NEXT:    blx r0
+; XO-LONGCALL-NEXT:    pop {r11, pc}
+;
+; PIC-LONGCALL-LABEL: test_global:
+; PIC-LONGCALL:       @ %bb.0: @ %entry
+; PIC-LONGCALL-NEXT:    .save {r11, lr}
+; PIC-LONGCALL-NEXT:    push {r11, lr}
+; PIC-LONGCALL-NEXT:    ldr r0, .LCPI3_0
+; PIC-LONGCALL-NEXT:  .LPC3_0:
+; PIC-LONGCALL-NEXT:    add r0, pc, r0
+; PIC-LONGCALL-NEXT:    blx r0
+; PIC-LONGCALL-NEXT:    pop {r11, pc}
+; PIC-LONGCALL-NEXT:    .p2align 2
+; PIC-LONGCALL-NEXT:  @ %bb.1:
+; PIC-LONGCALL-NEXT:  .LCPI3_0:
+; PIC-LONGCALL-NEXT:    .long .Lglobal_func$local-(.LPC3_0+8)
+entry:
+  call void @global_func()
+  ret void
+}
+
+declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg)
+
+define void @test_memset(ptr %dst, i8 %val, i32 %len) {
+; NO-OPTION-LABEL: test_memset:
+; NO-OPTION:       @ %bb.0: @ %entry
+; NO-OPTION-NEXT:    push {r7, lr}
+; NO-OPTION-NEXT:    bl memset
+; NO-OPTION-NEXT:    pop {r7, pc}
+;
+; LONGCALL-LABEL: test_memset:
+; LONGCALL:       @ %bb.0: @ %entry
+; LONGCALL-NEXT:    push {r7, lr}
+; LONGCALL-NEXT:    ldr r3, .LCPI4_0
+; LONGCALL-NEXT:    blx r3
+; LONGCALL-NEXT:    pop {r7, pc}
+; LONGCALL-NEXT:    .p2align 2
+; LONGCALL-NEXT:  @ %bb.1:
+; LONGCALL-NEXT:  .LCPI4_0:
+; LONGCALL-NEXT:    .long memset
+;
+; NO-LONGCALL-LABEL: test_memset:
+; NO-LONGCALL:       @ %bb.0: @ %entry
+; NO-LONGCALL-NEXT:    push {r7, lr}
+; NO-LONGCALL-NEXT:    bl memset
+; NO-LONGCALL-NEXT:    pop {r7, pc}
+;
+; XO-LONGCALL-LABEL: test_memset:
+; XO-LONGCALL:       @ %bb.0: @ %entry
+; XO-LONGCALL-NEXT:    .save {r11, lr}
+; XO-LONGCALL-NEXT:    push {r11, lr}
+; XO-LONGCALL-NEXT:    movw r3, :lower16:memset
+; XO-LONGCALL-NEXT:    movt r3, :upper16:memset
+; XO-LONGCALL-NEXT:    blx r3
+; XO-LONGCALL-NEXT:    pop {r11, pc}
+;
+; PIC-LONGCALL-LABEL: test_memset:
+; PIC-LONGCALL:       @ %bb.0: @ %entry
+; PIC-LONGCALL-NEXT:    .save {r11, lr}
+; PIC-LONGCALL-NEXT:    push {r11, lr}
+; PIC-LONGCALL-NEXT:    ldr r3, .LCPI4_0
+; PIC-LONGCALL-NEXT:  .LPC4_0:
+; PIC-LONGCALL-NEXT:    ldr r3, [pc, r3]
+; PIC-LONGCALL-NEXT:    blx r3
+; PIC-LONGCALL-NEXT:    pop {r11, pc}
+; PIC-LONGCALL-NEXT:    .p2align 2
+; PIC-LONGCALL-NEXT:  @ %bb.1:
+; PIC-LONGCALL-NEXT:  .LCPI4_0:
+; PIC-LONGCALL-NEXT:    .long memset(GOT_PREL)
+entry:
+  call void @llvm.memset.p0.i32(ptr %dst, i8 %val, i32 %len, i1 false)
+  ret void
+}

>From c53cc10eba4dfb636d3e94a64d12a713b5ecd7ec Mon Sep 17 00:00:00 2001
From: jxz-hw <[email protected]>
Date: Sat, 13 Jun 2026 01:21:55 +0800
Subject: [PATCH 2/2] [ARM][Clang] Diagnose incompatible -mlong-calls
 combinations at the driver and function attribute level

- -mlong-calls is not compatible with execute-only + position-independent code.
Because execute-only uses absolute movw/movt, which conflicts with PIC's 
GOT-based addressing.
- -mlong-calls is not supported for ROPI or RWPI.
---
 .../clang/Basic/DiagnosticCommonKinds.td      |  5 +++
 clang/lib/CodeGen/Targets/ARM.cpp             | 31 +++++++++++++++++++
 clang/lib/Driver/ToolChains/Arch/ARM.cpp      |  8 +++++
 clang/lib/Driver/ToolChains/Clang.cpp         |  9 ++++++
 .../CodeGen/arm-long-calls-attr-conflict.c    | 12 +++++++
 clang/test/Driver/arm-long-calls-conflict.c   | 17 ++++++++++
 6 files changed, 82 insertions(+)
 create mode 100644 clang/test/CodeGen/arm-long-calls-attr-conflict.c
 create mode 100644 clang/test/Driver/arm-long-calls-conflict.c

diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td 
b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index f2ed2f4698b8d..8a72d39d2a467 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -367,6 +367,11 @@ def err_opt_unsupported_with_suggest : Error<
   "option '%0' is unsupported; consider using '%1'">;
 def err_invalid_feature_combination : Error<
   "invalid feature combination: %0">;
+def err_drv_long_calls_unsupported_with_execute_only_pic : Error<
+  "'-mlong-calls' with '-mexecute-only' is not supported for "
+  "position-independent code">;
+def err_drv_long_calls_unsupported_with_pi : Error<
+  "'-mlong-calls' is not supported with %select{ROPI|RWPI}0">;
 def warn_invalid_feature_combination : Warning<
   "invalid feature combination: %0">, 
InGroup<DiagGroup<"invalid-feature-combination">>;
 def warn_target_unrecognized_env : Warning<
diff --git a/clang/lib/CodeGen/Targets/ARM.cpp 
b/clang/lib/CodeGen/Targets/ARM.cpp
index c6435dd6f5a8b..74587fa189a58 100644
--- a/clang/lib/CodeGen/Targets/ARM.cpp
+++ b/clang/lib/CodeGen/Targets/ARM.cpp
@@ -173,6 +173,37 @@ class ARMTargetCodeGenInfo : public TargetCodeGenInfo {
       setBranchProtectionFnAttributes(BPI, (*Fn));
     }
 
+    if (FD) {
+      bool HasLongCalls = false, HasExecOnly = false;
+      if (Fn->hasFnAttribute("target-features")) {
+        StringRef Features =
+            Fn->getFnAttribute("target-features").getValueAsString();
+        HasLongCalls = Features.contains("+long-calls");
+        HasExecOnly = Features.contains("+execute-only");
+      }
+
+      if (HasLongCalls && HasExecOnly) {
+        auto RelocModel = CGM.getCodeGenOpts().RelocationModel;
+        if (RelocModel == llvm::Reloc::PIC_)
+          CGM.getDiags().Report(
+              D->getLocation(),
+              diag::err_drv_long_calls_unsupported_with_execute_only_pic);
+      }
+
+      if (HasLongCalls) {
+        auto RelocModel = CGM.getCodeGenOpts().RelocationModel;
+        if (RelocModel == llvm::Reloc::ROPI ||
+            RelocModel == llvm::Reloc::ROPI_RWPI)
+          CGM.getDiags().Report(D->getLocation(),
+                                diag::err_drv_long_calls_unsupported_with_pi)
+              << 0; // ROPI
+        else if (RelocModel == llvm::Reloc::RWPI)
+          CGM.getDiags().Report(D->getLocation(),
+                                diag::err_drv_long_calls_unsupported_with_pi)
+              << 1; // RWPI
+      }
+    }
+
     if (!FD || !FD->hasAttr<ARMInterruptAttr>())
       return;
 
diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.cpp 
b/clang/lib/Driver/ToolChains/Arch/ARM.cpp
index 7d9c1f0bd3d40..b5b8bd173926e 100644
--- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp
@@ -964,6 +964,14 @@ llvm::ARM::FPUKind arm::getARMTargetFeatures(const Driver 
&D,
     }
   }
 
+  // -mlong-calls + -mexecute-only + -fPIC/-fPIE is incompatible.
+  if (llvm::is_contained(Features, std::string("+long-calls")) &&
+      llvm::is_contained(Features, std::string("+execute-only"))) {
+    if (Args.getLastArg(options::OPT_fPIC, options::OPT_fpic, 
options::OPT_fPIE,
+                        options::OPT_fpie))
+      D.Diag(diag::err_drv_long_calls_unsupported_with_execute_only_pic);
+  }
+
   if (Arg *A = Args.getLastArg(options::OPT_mno_unaligned_access,
                                       options::OPT_munaligned_access,
                                       options::OPT_mstrict_align,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index c2ac478d84929..d33f64f038784 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5742,6 +5742,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
       !Args.hasArg(options::OPT_fallow_unsupported))
     D.Diag(diag::err_drv_ropi_incompatible_with_cxx);
 
+  // long-calls is not compatible with ROPI or RWPI.
+  if ((IsROPI || IsRWPI) && !Args.hasArg(options::OPT_fallow_unsupported)) {
+    if (Arg *A = Args.getLastArg(options::OPT_mlong_calls,
+                                 options::OPT_mno_long_calls))
+      if (A->getOption().matches(options::OPT_mlong_calls))
+        D.Diag(diag::err_drv_long_calls_unsupported_with_pi)
+            << (IsROPI ? 0 : 1); // 0=ROPI, 1=RWPI;
+  }
+
   const char *RMName = RelocationModelName(RelocationModel);
   if (RMName) {
     CmdArgs.push_back("-mrelocation-model");
diff --git a/clang/test/CodeGen/arm-long-calls-attr-conflict.c 
b/clang/test/CodeGen/arm-long-calls-attr-conflict.c
new file mode 100644
index 0000000000000..f83ff17103d9b
--- /dev/null
+++ b/clang/test/CodeGen/arm-long-calls-attr-conflict.c
@@ -0,0 +1,12 @@
+// REQUIRES: arm-registered-target
+// RUN: not %clang_cc1 -triple armv7a-none-eabi -mrelocation-model pic 
-emit-obj %s -o /dev/null 2>&1 | FileCheck --check-prefix=ERR-XO-PIC %s
+// ERR-XO-PIC: error: '-mlong-calls' with '-mexecute-only' is not supported 
for position-independent code
+
+void __attribute__((target("+long-calls,+execute-only")))
+call_xo_pic(void) {}
+
+// RUN: not %clang_cc1 -triple armv7a-none-eabi -mrelocation-model ropi 
-emit-obj %s -o /dev/null 2>&1 | FileCheck --check-prefix=ERR-ROPI %s
+// ERR-ROPI: error: '-mlong-calls' is not supported with ROPI
+
+void __attribute__((target("+long-calls")))
+call_ropi(void) {}
diff --git a/clang/test/Driver/arm-long-calls-conflict.c 
b/clang/test/Driver/arm-long-calls-conflict.c
new file mode 100644
index 0000000000000..5a63900804d78
--- /dev/null
+++ b/clang/test/Driver/arm-long-calls-conflict.c
@@ -0,0 +1,17 @@
+// RUN: not %clang --target=armv7a-none-eabi -mlong-calls -mexecute-only -fPIC 
-### %s 2>&1 | FileCheck --check-prefix=ERR-XO-PIC %s
+// ERR-XO-PIC: error: '-mlong-calls' with '-mexecute-only' is not supported 
for position-independent code
+
+// RUN: not %clang --target=armv7a-none-eabi -mlong-calls -mexecute-only -fPIE 
-### %s 2>&1 | FileCheck --check-prefix=ERR-XO-PIE %s
+// ERR-XO-PIE: error: '-mlong-calls' with '-mexecute-only' is not supported 
for position-independent code
+
+// RUN: %clang --target=armv7a-none-eabi -mlong-calls -mexecute-only -### %s 
2>&1 | FileCheck --check-prefix=OK-XO %s
+// OK-XO-NOT: error:
+
+// RUN: not %clang --target=armv7a-none-eabi -mlong-calls -fropi -### %s 2>&1 
| FileCheck --check-prefix=ERR-ROPI %s
+// ERR-ROPI: error: '-mlong-calls' is not supported with ROPI
+
+// RUN: not %clang --target=armv7a-none-eabi -mlong-calls -frwpi -### %s 2>&1 
| FileCheck --check-prefix=ERR-RWPI %s
+// ERR-RWPI: error: '-mlong-calls' is not supported with RWPI
+
+// RUN: %clang --target=armv7a-none-eabi -mlong-calls -fropi -mno-long-calls 
-### %s 2>&1 | FileCheck --check-prefix=OK %s
+// OK-NOT: error:

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to