Skip to content

Commit 21f8311

Browse files
committed
[lld-macho] Add very basic support for LTO
Just enough to consume some bitcode files and link them. There's more to be done around the symbol resolution API and the LTO config, but I don't yet understand what all the various LTO settings do... Reviewed By: #lld-macho, compnerd, smeenai, MaskRay Differential Revision: https://reviews.llvm.org/D90663
1 parent 6cf2443 commit 21f8311

File tree

9 files changed

+238
-10
lines changed

9 files changed

+238
-10
lines changed

lld/MachO/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_lld_library(lldMachO2
1212
ExportTrie.cpp
1313
InputFiles.cpp
1414
InputSection.cpp
15+
LTO.cpp
1516
MergedOutputSection.cpp
1617
ObjC.cpp
1718
OutputSection.cpp
@@ -26,8 +27,11 @@ add_lld_library(lldMachO2
2627
${LLVM_TARGETS_TO_BUILD}
2728
BinaryFormat
2829
Core
30+
LTO
31+
MC
2932
Object
3033
Option
34+
Passes
3135
Support
3236
TextAPI
3337

lld/MachO/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct Configuration {
3939
bool isPic = false;
4040
bool headerPadMaxInstallNames = false;
4141
bool searchDylibsFirst = false;
42+
bool saveTemps = false;
4243
uint32_t headerPad;
4344
llvm::StringRef installName;
4445
llvm::StringRef outputFile;

lld/MachO/Driver.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Config.h"
1111
#include "DriverUtils.h"
1212
#include "InputFiles.h"
13+
#include "LTO.h"
1314
#include "ObjC.h"
1415
#include "OutputSection.h"
1516
#include "OutputSegment.h"
@@ -30,13 +31,15 @@
3031
#include "llvm/ADT/StringRef.h"
3132
#include "llvm/BinaryFormat/MachO.h"
3233
#include "llvm/BinaryFormat/Magic.h"
34+
#include "llvm/LTO/LTO.h"
3335
#include "llvm/Object/Archive.h"
3436
#include "llvm/Option/ArgList.h"
3537
#include "llvm/Option/Option.h"
3638
#include "llvm/Support/FileSystem.h"
3739
#include "llvm/Support/Host.h"
3840
#include "llvm/Support/MemoryBuffer.h"
3941
#include "llvm/Support/Path.h"
42+
#include "llvm/Support/TargetSelect.h"
4043

4144
#include <algorithm>
4245

@@ -316,16 +319,18 @@ static InputFile *addFile(StringRef path) {
316319
newFile = make<DylibFile>(mbref);
317320
break;
318321
case file_magic::tapi_file: {
319-
Optional<DylibFile *> dylibFile = makeDylibFromTAPI(mbref);
320-
if (!dylibFile)
321-
return nullptr;
322-
newFile = *dylibFile;
322+
if (Optional<DylibFile *> dylibFile = makeDylibFromTAPI(mbref))
323+
newFile = *dylibFile;
323324
break;
324325
}
326+
case file_magic::bitcode:
327+
newFile = make<BitcodeFile>(mbref);
328+
break;
325329
default:
326330
error(path + ": unhandled file type");
327331
}
328-
inputFiles.push_back(newFile);
332+
if (newFile)
333+
inputFiles.push_back(newFile);
329334
return newFile;
330335
}
331336

@@ -455,6 +460,27 @@ static bool markSubLibrary(StringRef searchName) {
455460
return false;
456461
}
457462

463+
// This function is called on startup. We need this for LTO since
464+
// LTO calls LLVM functions to compile bitcode files to native code.
465+
// Technically this can be delayed until we read bitcode files, but
466+
// we don't bother to do lazily because the initialization is fast.
467+
static void initLLVM() {
468+
InitializeAllTargets();
469+
InitializeAllTargetMCs();
470+
InitializeAllAsmPrinters();
471+
InitializeAllAsmParsers();
472+
}
473+
474+
static void compileBitcodeFiles() {
475+
auto lto = make<BitcodeCompiler>();
476+
for (InputFile *file : inputFiles)
477+
if (auto *bitcodeFile = dyn_cast<BitcodeFile>(file))
478+
lto->add(*bitcodeFile);
479+
480+
for (ObjFile *file : lto->compile())
481+
inputFiles.push_back(file);
482+
}
483+
458484
// Replaces common symbols with defined symbols residing in __common sections.
459485
// This function must be called after all symbol names are resolved (i.e. after
460486
// all InputFiles have been loaded.) As a result, later operations won't see
@@ -612,6 +638,8 @@ bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
612638
config->searchDylibsFirst =
613639
(arg && arg->getOption().getID() == OPT_search_dylibs_first);
614640

641+
config->saveTemps = args.hasArg(OPT_save_temps);
642+
615643
if (args.hasArg(OPT_v)) {
616644
message(getLLDVersion());
617645
message(StringRef("Library search paths:") +
@@ -692,6 +720,8 @@ bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
692720
error("-sub_library " + searchName + " does not match a supplied dylib");
693721
}
694722

723+
initLLVM();
724+
compileBitcodeFiles();
695725
replaceCommonSymbols();
696726

697727
StringRef orderFile = args.getLastArgValue(OPT_order_file);

lld/MachO/InputFiles.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "lld/Common/Memory.h"
5959
#include "llvm/ADT/iterator.h"
6060
#include "llvm/BinaryFormat/MachO.h"
61+
#include "llvm/LTO/LTO.h"
6162
#include "llvm/Support/Endian.h"
6263
#include "llvm/Support/MemoryBuffer.h"
6364
#include "llvm/Support/Path.h"
@@ -565,6 +566,11 @@ void ArchiveFile::fetch(const object::Archive::Symbol &sym) {
565566
file->subsections.end());
566567
}
567568

569+
BitcodeFile::BitcodeFile(MemoryBufferRef mbref)
570+
: InputFile(BitcodeKind, mbref) {
571+
obj = check(lto::InputFile::create(mbref));
572+
}
573+
568574
// Returns "<internal>" or "baz.o".
569575
std::string lld::toString(const InputFile *file) {
570576
return file ? std::string(file->getName()) : "<internal>";

lld/MachO/InputFiles.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
#include <map>
2424
#include <vector>
2525

26+
namespace llvm {
27+
namespace lto {
28+
class InputFile;
29+
} // namespace lto
30+
} // namespace llvm
31+
2632
namespace lld {
2733
namespace macho {
2834

@@ -39,9 +45,10 @@ class InputFile {
3945
public:
4046
enum Kind {
4147
ObjKind,
48+
OpaqueKind,
4249
DylibKind,
4350
ArchiveKind,
44-
OpaqueKind,
51+
BitcodeKind,
4552
};
4653

4754
virtual ~InputFile() = default;
@@ -127,6 +134,14 @@ class ArchiveFile : public InputFile {
127134
llvm::DenseSet<uint64_t> seen;
128135
};
129136

137+
class BitcodeFile : public InputFile {
138+
public:
139+
explicit BitcodeFile(MemoryBufferRef mb);
140+
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
141+
142+
std::unique_ptr<llvm::lto::InputFile> obj;
143+
};
144+
130145
extern std::vector<InputFile *> inputFiles;
131146

132147
llvm::Optional<MemoryBufferRef> readFile(StringRef path);

lld/MachO/LTO.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//===- LTO.cpp ------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "LTO.h"
10+
#include "Config.h"
11+
#include "InputFiles.h"
12+
13+
#include "lld/Common/ErrorHandler.h"
14+
#include "lld/Common/Strings.h"
15+
#include "lld/Common/TargetOptionsCommandFlags.h"
16+
#include "llvm/LTO/LTO.h"
17+
#include "llvm/Support/raw_ostream.h"
18+
19+
using namespace lld;
20+
using namespace lld::macho;
21+
using namespace llvm;
22+
23+
static lto::Config createConfig() {
24+
lto::Config c;
25+
c.Options = initTargetOptionsFromCodeGenFlags();
26+
return c;
27+
}
28+
29+
BitcodeCompiler::BitcodeCompiler() {
30+
auto backend =
31+
lto::createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
32+
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend);
33+
}
34+
35+
void BitcodeCompiler::add(BitcodeFile &f) {
36+
ArrayRef<lto::InputFile::Symbol> objSyms = f.obj->symbols();
37+
std::vector<lto::SymbolResolution> resols;
38+
resols.reserve(objSyms.size());
39+
40+
// Provide a resolution to the LTO API for each symbol.
41+
for (const lto::InputFile::Symbol &objSym : objSyms) {
42+
resols.emplace_back();
43+
lto::SymbolResolution &r = resols.back();
44+
45+
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
46+
// reports two symbols for module ASM defined. Without this check, lld
47+
// flags an undefined in IR with a definition in ASM as prevailing.
48+
// Once IRObjectFile is fixed to report only one symbol this hack can
49+
// be removed.
50+
r.Prevailing = !objSym.isUndefined();
51+
52+
// TODO: set the other resolution configs properly
53+
r.VisibleToRegularObj = true;
54+
}
55+
checkError(ltoObj->add(std::move(f.obj), resols));
56+
}
57+
58+
// Merge all the bitcode files we have seen, codegen the result
59+
// and return the resulting ObjectFile(s).
60+
std::vector<ObjFile *> BitcodeCompiler::compile() {
61+
unsigned maxTasks = ltoObj->getMaxTasks();
62+
buf.resize(maxTasks);
63+
64+
checkError(ltoObj->run([&](size_t task) {
65+
return std::make_unique<lto::NativeObjectStream>(
66+
std::make_unique<raw_svector_ostream>(buf[task]));
67+
}));
68+
69+
if (config->saveTemps) {
70+
if (!buf[0].empty())
71+
saveBuffer(buf[0], config->outputFile + ".lto.o");
72+
for (unsigned i = 1; i != maxTasks; ++i)
73+
saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
74+
}
75+
76+
std::vector<ObjFile *> ret;
77+
for (unsigned i = 0; i != maxTasks; ++i)
78+
if (!buf[i].empty())
79+
ret.push_back(make<ObjFile>(MemoryBufferRef(buf[i], "lto.tmp")));
80+
81+
return ret;
82+
}

lld/MachO/LTO.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLD_MACHO_LTO_H
10+
#define LLD_MACHO_LTO_H
11+
12+
#include "llvm/ADT/SmallString.h"
13+
#include <memory>
14+
#include <vector>
15+
16+
namespace llvm {
17+
namespace lto {
18+
class LTO;
19+
} // namespace lto
20+
} // namespace llvm
21+
22+
namespace lld {
23+
namespace macho {
24+
25+
class BitcodeFile;
26+
class ObjFile;
27+
28+
class BitcodeCompiler {
29+
public:
30+
BitcodeCompiler();
31+
32+
void add(BitcodeFile &f);
33+
std::vector<ObjFile *> compile();
34+
35+
private:
36+
std::unique_ptr<llvm::lto::LTO> ltoObj;
37+
std::vector<llvm::SmallString<0>> buf;
38+
};
39+
40+
} // namespace macho
41+
} // namespace lld
42+
43+
#endif

lld/MachO/Options.td

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,9 @@ def dependency_info : Separate<["-"], "dependency_info">,
453453
HelpText<"Dump dependency info">,
454454
Flags<[HelpHidden]>,
455455
Group<grp_introspect>;
456+
def save_temps : Flag<["-"], "save-temps">,
457+
HelpText<"Save temporary files instead of deleting them">,
458+
Group<grp_introspect>;
456459

457460
def grp_symtab : OptionGroup<"symtab">, HelpText<"SYMBOL TABLE OPTIMIZATIONS">;
458461

@@ -1233,10 +1236,6 @@ def random_uuid : Flag<["-"], "random_uuid">,
12331236
HelpText<"This option is undocumented in ld64">,
12341237
Flags<[HelpHidden]>,
12351238
Group<grp_undocumented>;
1236-
def save_temps : Flag<["-"], "save-temps">,
1237-
HelpText<"This option is undocumented in ld64">,
1238-
Flags<[HelpHidden]>,
1239-
Group<grp_undocumented>;
12401239
def simulator_support : Flag<["-"], "simulator_support">,
12411240
HelpText<"This option is undocumented in ld64">,
12421241
Flags<[HelpHidden]>,

lld/test/MachO/lto-save-temps.ll

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
; REQUIRES: x86
2+
3+
; Test that we compile regular LTO inputs in a single task but handle ThinLTO
4+
; modules in separate tasks.
5+
6+
; RUN: rm -rf %t; split-file %s %t
7+
; RUN: llvm-as %t/foo.ll -o %t/foo.o
8+
; RUN: llvm-as %t/test.ll -o %t/test.o
9+
; RUN: %lld -save-temps %t/foo.o %t/test.o -o %t/test
10+
; RUN: llvm-objdump -d --no-show-raw-insn %t/test.lto.o | FileCheck %s --check-prefix=ALL
11+
; RUN: llvm-objdump -d --no-show-raw-insn %t/test | FileCheck %s --check-prefix=ALL
12+
13+
; RUN: rm -rf %t; split-file %s %t
14+
; RUN: opt -module-summary %t/foo.ll -o %t/foo.o
15+
; RUN: opt -module-summary %t/test.ll -o %t/test.o
16+
; RUN: %lld -save-temps %t/foo.o %t/test.o -o %t/test
17+
; RUN: llvm-objdump -d --no-show-raw-insn %t/test1.lto.o | FileCheck %s --check-prefix=FOO
18+
; RUN: llvm-objdump -d --no-show-raw-insn %t/test2.lto.o | FileCheck %s --check-prefix=MAIN
19+
; RUN: llvm-objdump -d --no-show-raw-insn %t/test | FileCheck %s --check-prefix=ALL
20+
21+
; FOO: <_foo>:
22+
; FOO-NEXT: retq
23+
24+
; MAIN: <_main>:
25+
; MAIN-NEXT: retq
26+
27+
; ALL: <_foo>:
28+
; ALL-NEXT: retq
29+
; ALL: <_main>:
30+
; ALL-NEXT: retq
31+
32+
;--- foo.ll
33+
34+
target triple = "x86_64-apple-macosx10.15.0"
35+
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
36+
37+
define void @foo() {
38+
ret void
39+
}
40+
41+
;--- test.ll
42+
43+
target triple = "x86_64-apple-macosx10.15.0"
44+
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
45+
46+
define void @main() {
47+
ret void
48+
}

0 commit comments

Comments
 (0)