Skip to content

Commit 3a45b84

Browse files
committed
[lld] Preliminary fat-lto-object support
This patch adds support to lld for --fat-lto-objects. We add a new --fat-lto-objects flag to LLD, and slightly change how it chooses input files in the driver when the flag is set. Fat LTO objects contain both LTO compatible IR, as well as generated object code. This allows users to defer the choice of whether to use LTO or not to link-time. This is a feature available in GCC for some time, and makes the existing -ffat-lto-objects flag functional in the same way as GCC's. If the --fat-lto-objects option is passed to LLD and the input files are fat object files, then the linker will chose the LTO compatible bitcode sections embedded within the fat object and link them together using LTO. Otherwise, standard object file linking is done using the assembly section in the object files. Original RFC: https://discourse.llvm.org/t/rfc-ffat-lto-objects-support/63977 Depends on D146777 Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D146778
1 parent 5fdf860 commit 3a45b84

File tree

7 files changed

+146
-6
lines changed

7 files changed

+146
-6
lines changed

lld/ELF/Config.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ class LinkerDriver {
125125
void inferMachineType();
126126
void link(llvm::opt::InputArgList &args);
127127
template <class ELFT> void compileBitcodeFiles(bool skipLinkedOutput);
128-
128+
bool tryAddFatLTOFile(MemoryBufferRef mb, StringRef archiveName,
129+
uint64_t offsetInArchive, bool lazy);
129130
// True if we are in --whole-archive and --no-whole-archive.
130131
bool inWholeArchive = false;
131132

@@ -205,6 +206,7 @@ struct Config {
205206
callGraphProfile;
206207
bool cmseImplib = false;
207208
bool allowMultipleDefinition;
209+
bool fatLTOObjects;
208210
bool androidPackDynRelocs = false;
209211
bool armHasBlx = false;
210212
bool armHasMovtMovw = false;

lld/ELF/Driver.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "llvm/Config/llvm-config.h"
5353
#include "llvm/LTO/LTO.h"
5454
#include "llvm/Object/Archive.h"
55+
#include "llvm/Object/IRObjectFile.h"
5556
#include "llvm/Remarks/HotnessThresholdParser.h"
5657
#include "llvm/Support/CommandLine.h"
5758
#include "llvm/Support/Compression.h"
@@ -237,6 +238,19 @@ static bool isBitcode(MemoryBufferRef mb) {
237238
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
238239
}
239240

241+
bool LinkerDriver::tryAddFatLTOFile(MemoryBufferRef mb, StringRef archiveName,
242+
uint64_t offsetInArchive, bool lazy) {
243+
if (!config->fatLTOObjects)
244+
return false;
245+
Expected<MemoryBufferRef> fatLTOData =
246+
IRObjectFile::findBitcodeInMemBuffer(mb);
247+
if (errorToBool(fatLTOData.takeError()))
248+
return false;
249+
files.push_back(
250+
make<BitcodeFile>(*fatLTOData, archiveName, offsetInArchive, lazy));
251+
return true;
252+
}
253+
240254
// Opens a file and create a file object. Path has to be resolved already.
241255
void LinkerDriver::addFile(StringRef path, bool withLOption) {
242256
using namespace sys::fs;
@@ -261,7 +275,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
261275
for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
262276
if (isBitcode(p.first))
263277
files.push_back(make<BitcodeFile>(p.first, path, p.second, false));
264-
else
278+
else if (!tryAddFatLTOFile(p.first, path, p.second, false))
265279
files.push_back(createObjFile(p.first, path));
266280
}
267281
return;
@@ -285,9 +299,10 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
285299
InputFile::isInGroup = true;
286300
for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
287301
auto magic = identify_magic(p.first.getBuffer());
288-
if (magic == file_magic::elf_relocatable)
289-
files.push_back(createObjFile(p.first, path, true));
290-
else if (magic == file_magic::bitcode)
302+
if (magic == file_magic::elf_relocatable) {
303+
if (!tryAddFatLTOFile(p.first, path, p.second, true))
304+
files.push_back(createObjFile(p.first, path, true));
305+
} else if (magic == file_magic::bitcode)
291306
files.push_back(make<BitcodeFile>(p.first, path, p.second, true));
292307
else
293308
warn(path + ": archive member '" + p.first.getBufferIdentifier() +
@@ -319,7 +334,8 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
319334
files.push_back(make<BitcodeFile>(mbref, "", 0, inLib));
320335
break;
321336
case file_magic::elf_relocatable:
322-
files.push_back(createObjFile(mbref, "", inLib));
337+
if (!tryAddFatLTOFile(mbref, "", 0, inLib))
338+
files.push_back(createObjFile(mbref, "", inLib));
323339
break;
324340
default:
325341
error(path + ": unknown file type");
@@ -1138,6 +1154,8 @@ static void readConfigs(opt::InputArgList &args) {
11381154
args.hasFlag(OPT_android_memtag_heap, OPT_no_android_memtag_heap, false);
11391155
config->androidMemtagStack = args.hasFlag(OPT_android_memtag_stack,
11401156
OPT_no_android_memtag_stack, false);
1157+
config->fatLTOObjects =
1158+
args.hasFlag(OPT_fat_lto_objects, OPT_no_fat_lto_objects, false);
11411159
config->androidMemtagMode = getMemtagMode(args);
11421160
config->auxiliaryList = args::getStrings(args, OPT_auxiliary);
11431161
config->armBe8 = args.hasArg(OPT_be8);

lld/ELF/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,10 @@ def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">;
647647
def thinlto_single_module_eq: JJ<"thinlto-single-module=">,
648648
HelpText<"Specify a single module to compile in ThinLTO mode, for debugging only">;
649649

650+
defm fat_lto_objects: BB<"fat-lto-objects",
651+
"Use the .llvm.lto section, which contains LLVM bitcode, in fat LTO object files to perform LTO.",
652+
"Ignore the .llvm.lto section in relocatable object files (default).">;
653+
650654
def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for --lto-O">;
651655
def: F<"plugin-opt=debug-pass-manager">,
652656
Alias<lto_debug_pass_manager>, HelpText<"Alias for --lto-debug-pass-manager">;

lld/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ ELF Improvements
3131
* ``PT_RISCV_ATTRIBUTES`` is added to include the SHT_RISCV_ATTRIBUTES section.
3232
(`D152065 <https://reviews.llvm.org/D152065>`_)
3333

34+
- ``--fat-lto-objects`` option is added to support LLVM FatLTO.
35+
Without ``--fat-lto-objects``, LLD will link LLVM FatLTO objects using the
36+
relocatable object file. (`D146778 <https://reviews.llvm.org/D146778>`_)
37+
3438
Breaking changes
3539
----------------
3640

lld/docs/ld.lld.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,10 @@ Number of threads.
621621
(default) means all of concurrent threads supported.
622622
.Cm 1
623623
disables multi-threading.
624+
.It Fl -fat-lto-objects
625+
Use the .llvm.lto section, which contains LLVM bitcode, in fat LTO object files to perform LTO.
626+
.It Fl -no-fat-lto-objects
627+
Ignore the .llvm.lto section in relocatable object files (default).
624628
.It Fl -time-trace
625629
Record time trace.
626630
.It Fl -time-trace-file Ns = Ns Ar file

lld/test/ELF/fatlto/fatlto.invalid.s

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
2+
# RUN: not ld.lld %t -o /dev/null --fat-lto-objects 2>&1 | FileCheck %s
3+
4+
# CHECK: error:{{.*}} Invalid bitcode signature
5+
6+
.section .llvm.lto,"e",@progbits
7+
.Lllvm.embedded.object:
8+
.asciz "BC\300\3365\000"
9+
.size .Lllvm.embedded.object, 12

lld/test/ELF/fatlto/fatlto.test

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
;; Basic FatLTO tests.
2+
; REQUIRES: x86
3+
4+
; RUN: rm -rf %t && split-file %s %t
5+
6+
;; Ensure that input files contain .llvm.lto section.
7+
; RUN: llc %t/a-LTO.ll --filetype=obj -o %t/a-fatLTO.o
8+
; RUN: opt < %t/a-LTO.ll --module-summary -o %t/a-fatLTO.bc
9+
; RUN: llvm-objcopy --add-section=.llvm.lto=%t/a-fatLTO.bc --set-section-flags=.llvm.lto=exclude %t/a-fatLTO.o
10+
11+
; RUN: llc %t/main-LTO.ll --filetype=obj -o %t/main-fatLTO.o
12+
; RUN: opt < %t/main-LTO.ll --module-summary -o %t/main-fatLTO.bc
13+
; RUN: llvm-objcopy --add-section=.llvm.lto=%t/main-fatLTO.bc --set-section-flags=.llvm.lto=exclude %t/main-fatLTO.o
14+
15+
;; Final executable should not have .llvm.lto section no matter what the target is.
16+
; RUN: ld.lld -o %t/foo-fatLTO %t/a-fatLTO.o %t/main-fatLTO.o --fat-lto-objects
17+
; RUN: llvm-readobj -S %t/foo-fatLTO | FileCheck --check-prefix=CHECK-LTO-TARGET %s
18+
19+
;; Check that fat objects work w/ --start-lib.
20+
; RUN: ld.lld -o %t/foo-fatLTO.start_lib --start-lib %t/a-fatLTO.o %t/main-fatLTO.o --fat-lto-objects
21+
; RUN: llvm-readobj -S %t/foo-fatLTO.start_lib | FileCheck --check-prefix=CHECK-LTO-TARGET %s
22+
23+
;; Check if .llvm.lto section gets aggregated in LTO target.
24+
; CHECK-LTO-TARGET-NOT: Name: .llvm.lto
25+
26+
;; Final executable should not have .llvm.lto section no matter what the target is.
27+
; RUN: ld.lld -o %t/foo-fatNoLTO %t/a-fatLTO.o %/t/main-fatLTO.o
28+
; RUN: llvm-readobj -S %t/foo-fatNoLTO | FileCheck --check-prefix=CHECK-NON-LTO-TARGET %s
29+
30+
;; Check if .llvm.lto section gets aggregated in non-LTO target.
31+
; CHECK-NON-LTO-TARGET-NOT: Name: .llvm.lto
32+
33+
;; Check if the LTO target executable produced from FatLTO object file is
34+
;; identical to the one produced from LTO modules.
35+
; RUN: opt < %t/a-LTO.ll --module-summary -o %t/a-LTO.bc
36+
; RUN: opt < %t/main-LTO.ll --module-summary -o %t/main-LTO.bc
37+
; RUN: ld.lld -o %t/foo-LTO %t/a-LTO.bc %t/main-LTO.bc
38+
; RUN: cmp %t/foo-fatLTO %t/foo-LTO
39+
40+
;; Check if the no-LTO target executable produced from FatLTO object file is
41+
;; identical to the one produced from regular object files.
42+
; RUN: llc %t/a-LTO.ll --filetype=obj -o %t/a.o
43+
; RUN: llc %t/main-LTO.ll --filetype=obj -o %t/main.o
44+
; RUN: ld.lld -o %t/foo-noLTO %t/a.o %t/main.o
45+
; RUN: cmp %t/foo-fatNoLTO %t/foo-noLTO
46+
47+
;; Check archive support.
48+
; RUN: llvm-ar rcs %t/a.a %t/a-fatLTO.o
49+
; RUN: ld.lld -o %t/foo-fatLTO.archive %t/a.a %t/main-LTO.bc --fat-lto-objects
50+
; RUN: cmp %t/foo-fatLTO.archive %t/foo-LTO
51+
52+
;--- a-LTO.ll
53+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
54+
target triple = "x86_64-unknown-linux-gnu"
55+
56+
; Function Attrs: noinline nounwind uwtable
57+
define dso_local i32 @_start() #0 {
58+
entry:
59+
ret i32 0
60+
}
61+
62+
attributes #0 = { noinline nounwind uwtable }
63+
64+
!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6}
65+
66+
!0 = !{i32 1, !"wchar_size", i32 4}
67+
!1 = !{i32 7, !"PIC Level", i32 2}
68+
!2 = !{i32 7, !"PIE Level", i32 2}
69+
!3 = !{i32 7, !"uwtable", i32 2}
70+
!4 = !{i32 7, !"frame-pointer", i32 2}
71+
!5 = !{i32 1, !"ThinLTO", i32 0}
72+
!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
73+
74+
;--- main-LTO.ll
75+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
76+
target triple = "x86_64-unknown-linux-gnu"
77+
78+
; Function Attrs: noinline nounwind uwtable
79+
define dso_local i32 @main() #0 {
80+
entry:
81+
%retval = alloca i32, align 4
82+
store i32 0, ptr %retval, align 4
83+
%call = call i32 (...) @_start()
84+
ret i32 %call
85+
}
86+
87+
declare i32 @_start(...)
88+
89+
attributes #0 = { noinline nounwind uwtable }
90+
91+
!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6}
92+
93+
!0 = !{i32 1, !"wchar_size", i32 4}
94+
!1 = !{i32 7, !"PIC Level", i32 2}
95+
!2 = !{i32 7, !"PIE Level", i32 2}
96+
!3 = !{i32 7, !"uwtable", i32 2}
97+
!4 = !{i32 7, !"frame-pointer", i32 2}
98+
!5 = !{i32 1, !"ThinLTO", i32 0}
99+
!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1}

0 commit comments

Comments
 (0)