Skip to content

Commit 14e3bec

Browse files
committed
Reland "[lld] Preliminary fat-lto-object support"
This patch adds support to lld for --fat-lto-objects. We add a new --fat-lto-objects option to LLD, and slightly change how it chooses input files in the driver when the option 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 option 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. The previous version of this patch had a missing `REQUIRES: x86` line in `fatlto.invalid.s`. Additionally, it was reported that this patch caused a test failure in `export-dynamic-symbols.s`, however, 29112a9 disabled the `export-dynamic-symbols.s` test on Windows due to a quotation difference between platforms, unrelated to this patch. Original RFC: https://discourse.llvm.org/t/rfc-ffat-lto-objects-support/63977 Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D146778
1 parent b6e148f commit 14e3bec

File tree

7 files changed

+148
-6
lines changed

7 files changed

+148
-6
lines changed

lld/ELF/Config.h

+3-1
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

+23-5
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"
@@ -239,6 +240,19 @@ static bool isBitcode(MemoryBufferRef mb) {
239240
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
240241
}
241242

243+
bool LinkerDriver::tryAddFatLTOFile(MemoryBufferRef mb, StringRef archiveName,
244+
uint64_t offsetInArchive, bool lazy) {
245+
if (!config->fatLTOObjects)
246+
return false;
247+
Expected<MemoryBufferRef> fatLTOData =
248+
IRObjectFile::findBitcodeInMemBuffer(mb);
249+
if (errorToBool(fatLTOData.takeError()))
250+
return false;
251+
files.push_back(
252+
make<BitcodeFile>(*fatLTOData, archiveName, offsetInArchive, lazy));
253+
return true;
254+
}
255+
242256
// Opens a file and create a file object. Path has to be resolved already.
243257
void LinkerDriver::addFile(StringRef path, bool withLOption) {
244258
using namespace sys::fs;
@@ -263,7 +277,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
263277
for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
264278
if (isBitcode(p.first))
265279
files.push_back(make<BitcodeFile>(p.first, path, p.second, false));
266-
else
280+
else if (!tryAddFatLTOFile(p.first, path, p.second, false))
267281
files.push_back(createObjFile(p.first, path));
268282
}
269283
return;
@@ -287,9 +301,10 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
287301
InputFile::isInGroup = true;
288302
for (const std::pair<MemoryBufferRef, uint64_t> &p : members) {
289303
auto magic = identify_magic(p.first.getBuffer());
290-
if (magic == file_magic::elf_relocatable)
291-
files.push_back(createObjFile(p.first, path, true));
292-
else if (magic == file_magic::bitcode)
304+
if (magic == file_magic::elf_relocatable) {
305+
if (!tryAddFatLTOFile(p.first, path, p.second, true))
306+
files.push_back(createObjFile(p.first, path, true));
307+
} else if (magic == file_magic::bitcode)
293308
files.push_back(make<BitcodeFile>(p.first, path, p.second, true));
294309
else
295310
warn(path + ": archive member '" + p.first.getBufferIdentifier() +
@@ -321,7 +336,8 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
321336
files.push_back(make<BitcodeFile>(mbref, "", 0, inLib));
322337
break;
323338
case file_magic::elf_relocatable:
324-
files.push_back(createObjFile(mbref, "", inLib));
339+
if (!tryAddFatLTOFile(mbref, "", 0, inLib))
340+
files.push_back(createObjFile(mbref, "", inLib));
325341
break;
326342
default:
327343
error(path + ": unknown file type");
@@ -1134,6 +1150,8 @@ static void readConfigs(opt::InputArgList &args) {
11341150
args.hasFlag(OPT_android_memtag_heap, OPT_no_android_memtag_heap, false);
11351151
config->androidMemtagStack = args.hasFlag(OPT_android_memtag_stack,
11361152
OPT_no_android_memtag_stack, false);
1153+
config->fatLTOObjects =
1154+
args.hasFlag(OPT_fat_lto_objects, OPT_no_fat_lto_objects, false);
11371155
config->androidMemtagMode = getMemtagMode(args);
11381156
config->auxiliaryList = args::getStrings(args, OPT_auxiliary);
11391157
config->armBe8 = args.hasArg(OPT_be8);

lld/ELF/Options.td

+4
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

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ Non-comprehensive list of changes in this release
2626
ELF Improvements
2727
----------------
2828

29+
* ``--fat-lto-objects`` option is added to support LLVM FatLTO.
30+
Without ``--fat-lto-objects``, LLD will link LLVM FatLTO objects using the
31+
relocatable object file. (`D146778 <https://reviews.llvm.org/D146778>`_)
32+
33+
2934
Breaking changes
3035
----------------
3136

lld/docs/ld.lld.1

+4
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

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

lld/test/ELF/fatlto/fatlto.test

+99
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)