Skip to content

Commit 7601bc3

Browse files
committed
Initial DTLTO commit
This follows on from our original RFC: here This PR includes a design document which details the DTLTO design. There are some areas where we would like feedback from the upstream community to inform the design, those areas are noted in the design document. This first commit just adds the initial scaffolding to show how DTLTO will work end-to-end. Support for additional features such as archive handling/caching etc.. will be added in later commits. Once this commit is in place we intend to add those features in a series of subsequent commits. Questions to discuss with upstream contributors: - What can we call the feature? DTLTO doesn't really distinguish this from Bazel-style DTLTO. Bazel-style DTLTO doesn't mean anything to anyone else! - Perhaps there is a subset of arguments that should be automatically passed to the codegen tool from the driver level. For example perhaps the compiler should pass the relevant -mllm options to LLD if it sees: -fdiagnostics-format=msvc? - Where to store temporary files? In particular we might want to discuss our downstream performance optimization (not included in this patch) for creating the native object files in the cache so that when we rename them to populate the cache we are guaranteed not to cross a device (which caused a measurable performance problem downstream). - Our proposals for how to support bitcode archive members. - Perhaps the (current) default tool should be clang -cc1 instead of just clang.
1 parent f1a29ec commit 7601bc3

File tree

31 files changed

+2215
-15
lines changed

31 files changed

+2215
-15
lines changed

clang/docs/ThinLTO.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,33 @@ The ``BOOTSTRAP_LLVM_ENABLE_LTO=Thin`` will enable ThinLTO for stage 2 and
240240
stage 3 in case the compiler used for stage 1 does not support the ThinLTO
241241
option.
242242

243+
Distributed ThinLTO (DTLTO)
244+
---------------------------
245+
246+
DTLTO allows for the distribution of backend ThinLTO compilations
247+
internally as part of the link step and therefore should be usable
248+
via any build that can use ThinLTO.
249+
250+
DTLTO requires the LLD linker (-fuse-ld=lld).
251+
252+
-fthinlto-distributor=<path>
253+
- Specifies the <path> to the distributor process executable.
254+
255+
-Xdist <arg>
256+
- Pass <arg> to the distributor process (see -fthinlto-distributor=).
257+
- Can be specified multiple times to pass multiple options.
258+
259+
Examples:
260+
clang -flto=thin -fthinlto-distributor=sndbs-dtlto.exe -Xdist --verbose -fuse-ld=lld
261+
clang -flto=thin -fthinlto-distributor=%python -Xdist %llvm_src_root/utils/dtlto/local.py -fuse-ld=lld
262+
263+
Example distributor scripts can be found under `llvm/utils/dtlto`.
264+
265+
Clang supplies the the path to itself to LLD, so that it can be invoked
266+
to perform the backend compilations.
267+
268+
See `DTLTO <https://lld.llvm.org/dtlto.html>`_ for more information.
269+
243270
More Information
244271
================
245272

clang/include/clang/Driver/Options.td

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,9 @@ def offload_link : Flag<["--"], "offload-link">, Group<Link_Group>,
10411041
def Xlinker : Separate<["-"], "Xlinker">, Flags<[LinkerInput, RenderAsInput]>,
10421042
HelpText<"Pass <arg> to the linker">, MetaVarName<"<arg>">,
10431043
Group<Link_Group>;
1044+
def Xdist : Separate<["-"], "Xdist">, Flags<[LinkerInput, RenderAsInput]>,
1045+
HelpText<"Pass <arg> to the ThinLTO distributor">, MetaVarName<"<arg>">,
1046+
Group<Link_Group>;
10441047
def Xoffload_linker : JoinedAndSeparate<["-"], "Xoffload-linker">,
10451048
HelpText<"Pass <arg> to the offload linkers or the ones idenfied by -<triple>">,
10461049
MetaVarName<"<triple> <arg>">, Group<Link_Group>;
@@ -3997,7 +4000,9 @@ def ffinite_loops: Flag<["-"], "ffinite-loops">, Group<f_Group>,
39974000
def fno_finite_loops: Flag<["-"], "fno-finite-loops">, Group<f_Group>,
39984001
HelpText<"Do not assume that any loop is finite.">,
39994002
Visibility<[ClangOption, CC1Option]>;
4000-
4003+
def fthinlto_distributor_EQ : Joined<["-", "--"], "fthinlto-distributor=">,
4004+
HelpText<"Specifies the <path> to the distributor process executable.">, MetaVarName<"<path>">,
4005+
Visibility<[ClangOption, CLOption]>;
40014006
def ftrigraphs : Flag<["-"], "ftrigraphs">, Group<f_Group>,
40024007
HelpText<"Process trigraph sequences">, Visibility<[ClangOption, CC1Option]>;
40034008
def fno_trigraphs : Flag<["-"], "fno-trigraphs">, Group<f_Group>,

clang/lib/Driver/ToolChains/CommonArgs.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,15 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args,
859859
CmdArgs.push_back("--fat-lto-objects");
860860
}
861861

862+
if (Arg *A = Args.getLastArg(options::OPT_fthinlto_distributor_EQ)) {
863+
CmdArgs.push_back(Args.MakeArgString(Twine("--thinlto-distributor=") +
864+
A->getValue()));
865+
const char *ClangPath =
866+
Args.MakeArgString(ToolChain.getDriver().getClangProgramPath());
867+
CmdArgs.push_back(
868+
Args.MakeArgString(Twine("--thinlto-remote-opt-tool=") + ClangPath));
869+
}
870+
862871
const char *PluginOptPrefix = IsOSAIX ? "-bplugin_opt:" : "-plugin-opt=";
863872
const char *ExtraDash = IsOSAIX ? "-" : "";
864873
const char *ParallelismOpt = IsOSAIX ? "-threads=" : "jobs=";

cross-project-tests/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ set(CROSS_PROJECT_TEST_DEPS
2222
llvm-dwarfdump
2323
llvm-config
2424
llvm-objdump
25+
llvm-profdata
2526
split-file
2627
not
2728
)
@@ -94,6 +95,13 @@ add_lit_testsuite(check-cross-amdgpu "Running AMDGPU cross-project tests"
9495
DEPENDS clang
9596
)
9697

98+
# DTLTO tests.
99+
add_lit_testsuite(check-cross-dtlto "Running DTLTO cross-project tests"
100+
${CMAKE_CURRENT_BINARY_DIR}/dtlto
101+
EXCLUDE_FROM_CHECK_ALL
102+
DEPENDS ${CROSS_PROJECT_TEST_DEPS}
103+
)
104+
97105
# Add check-cross-project-* targets.
98106
add_lit_testsuites(CROSS_PROJECT ${CMAKE_CURRENT_SOURCE_DIR}
99107
DEPENDS ${CROSS_PROJECT_TEST_DEPS}

cross-project-tests/dtlto/README.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-*- rst -*-
2+
This is a collection of tests to check distributed thinLTO (DTLTO) functionality
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
## Test that --lto-cs-profile-file= can be specified.
2+
## This is interesting as it requires an additional input file.
3+
4+
# RUN: rm -rf %t.dir && split-file %s %t.dir && cd %t.dir
5+
6+
## compile bitcode.
7+
# RUN: echo "-triple x86_64-unknown-unknown -funified-lto -emit-llvm-bc -flto=thin" > c.rsp
8+
# RUN: %clang_cc1 -x c foo.c -o foo.o @c.rsp
9+
# RUN: %clang_cc1 -x c bar.c -o bar.o @c.rsp
10+
# RUN: %clang_cc1 -x c _start.c -o _start.o @c.rsp
11+
12+
## Create an empty profile.
13+
# RUN: touch empty.proftext
14+
# RUN: llvm-profdata merge empty.proftext -o empty.profdata
15+
16+
# RUN: echo "foo.o bar.o _start.o \
17+
# RUN: --build-id=none \
18+
# RUN: --lto=thin --lto-O3 \
19+
# RUN: --thinlto-distributor=%python \
20+
# RUN: -mllvm -thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \
21+
# RUN: --thinlto-remote-opt-tool=%clang" > l.rsp
22+
23+
## link passes with an empty profile.
24+
# RUN: ld.lld @l.rsp --lto-cs-profile-file=empty.profdata
25+
26+
## Generate a broken profile to show that the profile
27+
## is consumed by the backend compilations.
28+
# RUN: echo "WOBBLER" > bad.profdata
29+
# RUN: not ld.lld @l.rsp --lto-cs-profile-file=bad.profdata 2>&1 | FileCheck %s
30+
31+
# CHECK: Error in reading profile
32+
# CHECK: error: backend compilation error
33+
34+
#--- foo.c
35+
void foo() {}
36+
37+
#--- bar.c
38+
extern void foo();
39+
void bar() {foo();}
40+
41+
#--- _start.c
42+
extern void bar();
43+
void _start() {bar();}

cross-project-tests/dtlto/dtlto.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: rm -rf %t && mkdir -p %t && cd %t
2+
3+
// RUN: %clang -target x86_64-linux-gnu %s -flto=thin \
4+
// RUN: -fthinlto-distributor=%python \
5+
// RUN: -Xdist %llvm_src_root/utils/dtlto/local.py \
6+
// RUN: --save-temps \
7+
// RUN: -fuse-ld=lld \
8+
// RUN: -Wl,--save-temps \
9+
// RUN: -nostdlib \
10+
// RUN: -nostartfiles
11+
12+
/// Check that the required output files have been created.
13+
// RUN: ls | tee %t.log | count 20
14+
// RUN: ls | FileCheck %s --check-prefix=COMPILE
15+
// RUN: ls | FileCheck %s --check-prefix=DTLTO
16+
// RUN: ls | FileCheck %s --check-prefix=LTO
17+
18+
/// Files produced by the compiler.
19+
// COMPILE: dtlto.bc
20+
// COMPILE: dtlto.i
21+
// COMPILE: dtlto.o
22+
23+
/// DTLTO native object output file for t.o.
24+
// DTLTO: t{{.*}}.{{.*}}.native.o{{$}}
25+
/// DTLTO imports file for t.o.
26+
// DTLTO: {{.*}}.{{.*}}.native.o.imports{{$}}
27+
/// DTLTO summary slice for t.o.
28+
// DTLTO: t{{.*}}.{{.*}}.native.o.thinlto.bc{{$}}
29+
30+
// LTO: a.out{{$}}
31+
/// save-temps incremental files for a.out
32+
// LTO: a.out.0.0.preopt.bc{{$}}
33+
// LTO: a.out.0.2.internalize.bc{{$}}
34+
// LTO: a.out.dist-file.json{{$}}
35+
// LTO: a.out.index.bc{{$}}
36+
// LTO: a.out.index.dot{{$}}
37+
// : a.out.lto.dtlto.o{{$}}
38+
// LTO: a.out.resolution.txt{{$}}
39+
/// save-temps incremental files for t.o.
40+
// LTO: dtlto-{{.*}}.{{.*}}.native.o.0.preopt.bc{{$}}
41+
// LTO: dtlto-{{.*}}.{{.*}}.native.o.1.promote.bc{{$}}
42+
// LTO: dtlto-{{.*}}.{{.*}}.native.o.2.internalize.bc{{$}}
43+
// LTO: dtlto-{{.*}}.{{.*}}.native.o.3.import.bc{{$}}
44+
// LTO: dtlto-{{.*}}.{{.*}}.native.o.4.opt.bc{{$}}
45+
// LTO: dtlto-{{.*}}.{{.*}}.native.o.5.precodegen.bc{{$}}
46+
// LTO: dtlto-{{.*}}.{{.*}}.native.o.resolution.txt{{$}}
47+
48+
int _start() {return 0;}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
if "clang" not in config.available_features:
2+
config.unsupported = True

lld/ELF/Config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ struct Config {
206206
llvm::SmallVector<llvm::StringRef, 0> searchPaths;
207207
llvm::SmallVector<llvm::StringRef, 0> symbolOrderingFile;
208208
llvm::SmallVector<llvm::StringRef, 0> thinLTOModulesToCompile;
209+
llvm::StringRef DTLTODistributor;
210+
llvm::StringRef DTLTORemoteOptTool;
209211
llvm::SmallVector<llvm::StringRef, 0> undefined;
210212
llvm::SmallVector<SymbolVersion, 0> dynamicList;
211213
llvm::SmallVector<uint8_t, 0> buildIdVector;

lld/ELF/Driver.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,12 @@ static void readConfigs(opt::InputArgList &args) {
14431443
}
14441444
config->thinLTOModulesToCompile =
14451445
args::getStrings(args, OPT_thinlto_single_module_eq);
1446+
1447+
config->DTLTODistributor =
1448+
args.getLastArgValue(OPT_thinlto_distributor_eq);
1449+
config->DTLTORemoteOptTool =
1450+
args.getLastArgValue(OPT_thinlto_remote_opt_tool_eq);
1451+
14461452
config->timeTraceEnabled = args.hasArg(OPT_time_trace_eq);
14471453
config->timeTraceGranularity =
14481454
args::getInteger(args, OPT_time_trace_granularity, 500);
@@ -1624,6 +1630,22 @@ static void readConfigs(opt::InputArgList &args) {
16241630

16251631
config->passPlugins = args::getStrings(args, OPT_load_pass_plugins);
16261632

1633+
if (!config->DTLTODistributor.empty()) {
1634+
for (auto o : {"-thinlto-cc1-arg=-ferror-limit", "-thinlto-cc1-arg=19",
1635+
"-thinlto-cc1-arg=-fmessage-length=120"}) {
1636+
parseClangOption(o, "-mllvm");
1637+
config->mllvmOpts.emplace_back(o);
1638+
}
1639+
1640+
if (errorHandler().vsDiagnostics) {
1641+
for (auto o :
1642+
{"-thinlto-cc1-arg=-fdiagnostics-format", "-thinlto-cc1-arg=msvc"}) {
1643+
parseClangOption(o, "-mllvm");
1644+
config->mllvmOpts.emplace_back(o);
1645+
}
1646+
}
1647+
}
1648+
16271649
// Parse -mllvm options.
16281650
for (const auto *arg : args.filtered(OPT_mllvm)) {
16291651
parseClangOption(arg->getValue(), arg->getSpelling());

0 commit comments

Comments
 (0)