Skip to content

Commit fcf661d

Browse files
[AArch64] Add support for -mlong-calls code generation
This patch implements backend support for -mlong-calls on AArch64 targets. When enabled, calls to external functions are lowered to an indirect call via an address computed using `adrp` and `add` rather than a direct `bl` instruction, which is limited to a ±128MB PC-relative offset. This is particularly useful when code and/or data exceeds the 26-bit immediate range of `bl`, such as in large binaries or link-time-optimized builds. Key changes: - In SelectionDAG lowering (`LowerCall`), detect `-mlong-calls` and emit: - `adrp + add` address calculation - `blr` indirect call instruction This patch ensures that long-calls are emitted correctly for both GlobalAddress and ExternalSymbol call targets. Tested: - New codegen tests under `llvm/test/CodeGen/AArch64/aarch64-long-calls.ll` - Verified `adrp + add + blr` output in `.s` for global and external functions
1 parent 2b3e07f commit fcf661d

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

clang/lib/Driver/ToolChains/Arch/AArch64.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,12 @@ void aarch64::getAArch64TargetFeatures(const Driver &D,
466466

467467
if (Args.getLastArg(options::OPT_mno_bti_at_return_twice))
468468
Features.push_back("+no-bti-at-return-twice");
469+
470+
if (Arg *A = Args.getLastArg(options::OPT_mlong_calls,
471+
options::OPT_mno_long_calls)) {
472+
if (A->getOption().matches(options::OPT_mlong_calls))
473+
Features.push_back("+long-calls");
474+
}
469475
}
470476

471477
void aarch64::setPAuthABIInTriple(const Driver &D, const ArgList &Args,

llvm/lib/Target/AArch64/AArch64Features.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,10 @@ def FeatureDisableFastIncVL : SubtargetFeature<"disable-fast-inc-vl",
825825
"HasDisableFastIncVL", "true",
826826
"Do not prefer INC/DEC, ALL, { 1, 2, 4 } over ADDVL">;
827827

828+
def FeatureLongCalls : SubtargetFeature<"long-calls", "GenLongCalls", "true",
829+
"Generate calls via indirect call "
830+
"instructions">;
831+
828832
//===----------------------------------------------------------------------===//
829833
// Architectures.
830834
//

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9286,8 +9286,12 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
92869286
Callee = DAG.getTargetGlobalAddress(CalledGlobal, DL, PtrVT, 0, OpFlags);
92879287
Callee = DAG.getNode(AArch64ISD::LOADgot, DL, PtrVT, Callee);
92889288
} else {
9289-
const GlobalValue *GV = G->getGlobal();
9290-
Callee = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, OpFlags);
9289+
if (Subtarget->genLongCalls())
9290+
Callee = getAddr(G, DAG, OpFlags);
9291+
else {
9292+
const GlobalValue *GV = G->getGlobal();
9293+
Callee = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, OpFlags);
9294+
}
92919295
}
92929296
} else if (auto *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
92939297
bool UseGot = (getTargetMachine().getCodeModel() == CodeModel::Large &&
@@ -9298,7 +9302,10 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
92989302
Callee = DAG.getTargetExternalSymbol(Sym, PtrVT, AArch64II::MO_GOT);
92999303
Callee = DAG.getNode(AArch64ISD::LOADgot, DL, PtrVT, Callee);
93009304
} else {
9301-
Callee = DAG.getTargetExternalSymbol(Sym, PtrVT, 0);
9305+
if (Subtarget->genLongCalls())
9306+
Callee = getAddr(S, DAG, 0);
9307+
else
9308+
Callee = DAG.getTargetExternalSymbol(Sym, PtrVT, 0);
93029309
}
93039310
}
93049311

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
; RUN: llc -O2 -mtriple=aarch64-linux-gnu -mcpu=generic -mattr=+long-calls < %s | FileCheck %s
2+
3+
declare void @far_func()
4+
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
5+
6+
define void @test() {
7+
entry:
8+
call void @far_func()
9+
ret void
10+
}
11+
12+
define void @test2(ptr %dst, i8 %val, i64 %len) {
13+
entry:
14+
call void @llvm.memset.p0.i64(ptr %dst, i8 %val, i64 %len, i1 false)
15+
ret void
16+
}
17+
18+
; CHECK-LABEL: test:
19+
; CHECK: adrp {{x[0-9]+}}, far_func
20+
; CHECK: add {{x[0-9]+}}, {{x[0-9]+}}, :lo12:far_func
21+
; CHECK: blr {{x[0-9]+}}
22+
23+
; CHECK-LABEL: test2:
24+
; CHECK: adrp {{x[0-9]+}}, memset
25+
; CHECK: add {{x[0-9]+}}, {{x[0-9]+}}, :lo12:memset
26+
; CHECK: blr {{x[0-9]+}}

0 commit comments

Comments
 (0)