[clang] [llvm] [ARM] support -mlong-calls -fPIC on arm32 #39970 (PR #147313)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 13 09:33:37 PDT 2026
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 <jiangxuezhi2 at huawei.com>
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 <jiangxuezhi2 at huawei.com>
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:
More information about the cfe-commits
mailing list