Skip to content

Commit 3711d21

Browse files
committed
Implement integrated distribution for ThinLTO (DTLTO). ELF and COFF only.
1 parent 8ed3637 commit 3711d21

File tree

44 files changed

+1796
-75
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1796
-75
lines changed

clang/docs/ThinLTO.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,38 @@ 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 via external
247+
distribution systems, e.g. Incredibuild. There is existing support for
248+
distributing ThinLTO compilations by using separate thin-link, backend
249+
compilation, and link steps coordinated by a build system which can handle the
250+
dynamic dependencies specified by the index files, such as Bazel. However, this
251+
often requires changes to the user's build process. With DTLTO distribution is
252+
managed internally in LLD as part of the traditional link step and therefore
253+
should be usable in any build process that can support in-process ThinLTO.
254+
255+
DTLTO requires the LLD linker (``-fuse-ld=lld``).
256+
257+
``-fthinlto-distributor=<path>``
258+
- Specifies the ``<path>`` to the distributor process executable for DTLTO.
259+
- If specified, ThinLTO backend compilations will be distributed by LLD.
260+
261+
``-Xdist <arg>``
262+
- Pass ``<arg>`` to the distributor process (see ``-fthinlto-distributor=``).
263+
- Can be specified multiple times to pass multiple options.
264+
265+
Examples:
266+
- ``clang -flto=thin -fthinlto-distributor=incredibuild.exe -Xdist --verbose -fuse-ld=lld``
267+
- ``clang -flto=thin -fthinlto-distributor=$(which python) -Xdist incredibuild.py -fuse-ld=lld``
268+
269+
If ``-fthinlto-distributor=`` is specified Clang supplies the path to a
270+
distributable optimization and code generation tool to LLD. Currently this tool
271+
is Clang itself specified.
272+
273+
See `DTLTO <https://lld.llvm.org/dtlto.html>`_ for more information.
274+
243275
More Information
244276
================
245277

clang/include/clang/Driver/Options.td

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,10 @@ def Xlinker : Separate<["-"], "Xlinker">, Flags<[LinkerInput, RenderAsInput]>,
969969
Visibility<[ClangOption, CLOption, FlangOption]>,
970970
HelpText<"Pass <arg> to the linker">, MetaVarName<"<arg>">,
971971
Group<Link_Group>;
972+
def Xdist : Separate<["-"], "Xdist">, Flags<[LinkOption]>,
973+
Visibility<[ClangOption, CLOption]>,
974+
HelpText<"Pass <arg> to the ThinLTO distributor">,
975+
MetaVarName<"<arg>">, Group<Link_Group>;
972976
def Xoffload_linker : JoinedAndSeparate<["-"], "Xoffload-linker">,
973977
Visibility<[ClangOption, FlangOption]>,
974978
HelpText<"Pass <arg> to the offload linkers or the ones identified by -<triple>">,
@@ -4087,7 +4091,9 @@ def ffinite_loops: Flag<["-"], "ffinite-loops">, Group<f_Group>,
40874091
def fno_finite_loops: Flag<["-"], "fno-finite-loops">, Group<f_Group>,
40884092
HelpText<"Do not assume that any loop is finite.">,
40894093
Visibility<[ClangOption, CC1Option]>;
4090-
4094+
def fthinlto_distributor_EQ : Joined<["-"], "fthinlto-distributor=">, Group<f_Group>,
4095+
HelpText<"Specifies the <path> to the distributor process executable.">, MetaVarName<"<path>">,
4096+
Visibility<[ClangOption, CLOption]>;
40914097
def ftrigraphs : Flag<["-"], "ftrigraphs">, Group<f_Group>,
40924098
HelpText<"Process trigraph sequences">, Visibility<[ClangOption, CC1Option]>;
40934099
def fno_trigraphs : Flag<["-"], "fno-trigraphs">, Group<f_Group>,

clang/lib/Driver/ToolChains/Gnu.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,25 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
535535
D.getLTOMode() == LTOK_Thin);
536536
}
537537

538+
// Forward the DTLTO options to the linker. We add these unconditionally,
539+
// rather than in addLTOOptions() as it is the linker that decides whether to
540+
// do LTO or not dependent upon whether there are any bitcode input files in
541+
// the link.
542+
if (Arg *A = Args.getLastArg(options::OPT_fthinlto_distributor_EQ)) {
543+
A->claim();
544+
CmdArgs.push_back(
545+
Args.MakeArgString("--thinlto-distributor=" + Twine(A->getValue())));
546+
CmdArgs.push_back(
547+
Args.MakeArgString("--thinlto-remote-opt-tool=" +
548+
Twine(ToolChain.getDriver().getClangProgramPath())));
549+
550+
for (const Arg *A : Args.filtered(options::OPT_Xdist)) {
551+
A->claim();
552+
CmdArgs.push_back(Args.MakeArgString("-mllvm=-thinlto-distributor-arg=" +
553+
Twine(A->getValue())));
554+
}
555+
}
556+
538557
if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle))
539558
CmdArgs.push_back("--no-demangle");
540559

clang/test/Driver/DTLTO/dtlto.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/// Check DTLTO options are forwarded to the linker.
2+
3+
// REQUIRES: lld
4+
5+
// RUN: echo "-target x86_64-linux-gnu \
6+
// RUN: -Xdist distarg1 \
7+
// RUN: -Xdist distarg2 \
8+
// RUN: -fuse-ld=lld" > %t.rsp
9+
10+
11+
/// Check that options are forwarded as expected with --thinlto-distributor=.
12+
// RUN: %clang -### @%t.rsp -fthinlto-distributor=dist.exe %s 2>&1 | \
13+
// RUN: FileCheck %s --implicit-check-not=warning
14+
15+
// CHECK: ld.lld
16+
// CHECK-SAME: "--thinlto-distributor=dist.exe"
17+
// CHECK-SAME: "--thinlto-remote-opt-tool={{.*}}clang
18+
// CHECK-SAME: "-mllvm=-thinlto-distributor-arg=distarg1"
19+
// CHECK-SAME: "-mllvm=-thinlto-distributor-arg=distarg2"
20+
21+
22+
/// Check that options are not added without --thinlto-distributor= and
23+
/// that there is an unused option warning issued for -Xdist options. We
24+
/// specify -flto here as these options should be unaffected by it.
25+
// RUN: %clang -### @%t.rsp -flto=thin %s 2>&1 | \
26+
// RUN: FileCheck %s --check-prefixes=NONE,NOMORE --implicit-check-not=warning
27+
28+
// NONE: warning: argument unused during compilation: '-Xdist distarg1'
29+
// NONE: warning: argument unused during compilation: '-Xdist distarg2'
30+
// NONE: ld.lld
31+
// NOMORE-NOT: --thinlto-distributor=
32+
// NOMORE-NOT: --thinlto-remote-opt-tool=
33+
// NOMORE-NOT: -mllvm
34+
// NOMORE-NOT: -thinlto-distributor-arg=
35+
36+
37+
/// Check the expected arguments are forwarded by default with only
38+
/// --thinlto-distributor=.
39+
// RUN: %clang -### -target x86_64-linux-gnu -fthinlto-distributor=dist.exe -fuse-ld=lld %s 2>&1 | \
40+
// RUN: FileCheck %s --check-prefixes=DEFAULT,NOMORE --implicit-check-not=warning
41+
42+
// DEFAULT: ld.lld
43+
// DEFAULT-SAME: "--thinlto-distributor=dist.exe"
44+
// DEFAULT-SAME: "--thinlto-remote-opt-tool={{.*}}clang

clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ Expected<SmallVector<StringRef>> getInput(const ArgList &Args) {
645645
std::make_unique<raw_fd_ostream>(FD, true));
646646
};
647647

648-
if (Error Err = LTOBackend.run(AddStream))
648+
if (Error Err = LTOBackend.run(AddStream, /*AddBuffer=*/nullptr))
649649
return Err;
650650

651651
if (Args.hasArg(OPT_lto_emit_llvm) || Args.hasArg(OPT_lto_emit_asm))

cross-project-tests/CMakeLists.txt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ set(CROSS_PROJECT_TEST_DEPS
1919
FileCheck
2020
check-gdb-llvm-support
2121
count
22-
llvm-dwarfdump
22+
llvm-ar
2323
llvm-config
24+
llvm-dwarfdump
25+
llvm-lto2
2426
llvm-objdump
25-
split-file
27+
llvm-profdata
2628
not
29+
opt
30+
split-file
2731
)
2832

2933
if ("clang" IN_LIST LLVM_ENABLE_PROJECTS)
@@ -94,6 +98,13 @@ add_lit_testsuite(check-cross-amdgpu "Running AMDGPU cross-project tests"
9498
DEPENDS clang
9599
)
96100

101+
# DTLTO tests.
102+
add_lit_testsuite(check-cross-dtlto "Running DTLTO cross-project tests"
103+
${CMAKE_CURRENT_BINARY_DIR}/dtlto
104+
EXCLUDE_FROM_CHECK_ALL
105+
DEPENDS ${CROSS_PROJECT_TEST_DEPS}
106+
)
107+
97108
# Add check-cross-project-* targets.
98109
add_lit_testsuites(CROSS_PROJECT ${CMAKE_CURRENT_SOURCE_DIR}
99110
DEPENDS ${CROSS_PROJECT_TEST_DEPS}
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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
## Simple test that a DTLTO link succeeds and outputs the expected set of files
2+
## correctly when thin archives are present.
3+
4+
# RUN: rm -rf %t.dir && split-file %s %t.dir && cd %t.dir
5+
# RUN: %clang -target x86_64-linux-gnu -c foo.c -o foo.o
6+
# RUN: %clang -target x86_64-linux-gnu -c -flto=thin bar.c -o bar.o
7+
# RUN: %clang -target x86_64-linux-gnu -c -flto=thin dog.c -o dog.o
8+
# RUN: %clang -target x86_64-linux-gnu -c -flto=thin cat.c -o cat.o
9+
# RUN: %clang -target x86_64-linux-gnu -c -flto=thin _start.c -o _start.o
10+
11+
# RUN: llvm-ar rcs foo.a foo.o --thin
12+
## Create this bitcode thin archive in a sub-directory to test the expansion of
13+
## the path to a bitcode file which is referenced using "..", e.g. in this case
14+
## "../bar.o". The ".." should be collapsed in any expansion to avoid
15+
## referencing an unknown directory on the remote side.
16+
# RUN: mkdir lib
17+
# RUN: llvm-ar rcs lib/bar.a bar.o --thin
18+
## Create this bitcode thin archive with an absolute path entry containing "..".
19+
# RUN: llvm-ar rcs dog.a %t.dir/lib/../dog.o --thin
20+
# RUN: llvm-ar rcs cat.a cat.o --thin
21+
# RUN: llvm-ar rcs _start.a _start.o --thin
22+
23+
# RUN: mkdir %t.dir/out && cd %t.dir/out
24+
25+
# RUN: %clang -target x86_64-linux-gnu \
26+
# RUN: %t.dir/foo.a %t.dir/lib/bar.a ../_start.a %t.dir/cat.a -Wl,--whole-archive,../dog.a \
27+
# RUN: -flto=thin \
28+
# RUN: -fthinlto-distributor=%python \
29+
# RUN: -Xdist %llvm_src_root/utils/dtlto/local.py \
30+
# RUN: --save-temps \
31+
# RUN: -fuse-ld=lld \
32+
# RUN: -nostdlib \
33+
# RUN: -nostartfiles \
34+
# RUN: -Wl,--save-temps \
35+
# RUN: -Wl,-mllvm,--thinlto-remote-opt-tool-arg=-save-temps=cwd \
36+
# RUN: -Werror
37+
38+
## Check that the required output files have been created.
39+
# RUN: ls | FileCheck %s --check-prefix=OUTPUTS \
40+
# RUN: --implicit-check-not=cat --implicit-check-not=foo
41+
42+
## The DTLTO backend emits the JSON jobs description and summary shards.
43+
# OUTPUTS-DAG: a.{{[0-9]+}}.dist-file.json
44+
# OUTPUTS-DAG: bar.{{[0-9]+}}.{{[0-9]+}}.native.o.thinlto.bc{{$}}
45+
# OUTPUTS-DAG: dog.{{[0-9]+}}.{{[0-9]+}}.native.o.thinlto.bc{{$}}
46+
# OUTPUTS-DAG: _start.{{[0-9]+}}.{{[0-9]+}}.native.o.thinlto.bc{{$}}
47+
## Native output object files.
48+
# OUTPUTS-DAG: bar.{{[0-9]+}}.{{[0-9]+}}.native.o{{$}}
49+
# OUTPUTS-DAG: dog.{{[0-9]+}}.{{[0-9]+}}.native.o{{$}}
50+
# OUTPUTS-DAG: _start.{{[0-9]+}}.{{[0-9]+}}.native.o{{$}}
51+
52+
## Check that bar.o and dog.o are not referenced using "..".
53+
# RUN: not grep '\.\.\(/\|\\\\\)\(bar\|dog\)\.o' a.*.dist-file.json
54+
55+
#--- foo.c
56+
__attribute__((retain)) void foo() {}
57+
58+
#--- bar.c
59+
extern void foo();
60+
__attribute__((retain)) void bar() { foo(); }
61+
62+
#--- dog.c
63+
__attribute__((retain)) void dog() {}
64+
65+
#--- cat.c
66+
__attribute__((retain)) void cat() {}
67+
68+
#--- _start.c
69+
extern void bar();
70+
__attribute__((retain)) void _start() {
71+
bar();
72+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
;; Check that the expected Clang arguments are generated by DTLTO for the
2+
;; backend compilations and are accepted by Clang.
3+
4+
; RUN: rm -rf %t && split-file %s %t && cd %t
5+
6+
;; Generate bitcode files with a summary index.
7+
; RUN: opt -thinlto-bc x86_64-unknown-linux-gnu.ll -o x86_64-unknown-linux-gnu.bc
8+
; RUN: opt -thinlto-bc x86_64-pc-windows-msvc.ll -o x86_64-pc-windows-msvc.bc
9+
10+
11+
;; Check that any invalid arguments would cause a Clang error. This property is
12+
;; relied on by the actual testcases later in this test.
13+
; RUN: not %clang -x ir x86_64-unknown-linux-gnu.ll \
14+
; RUN: -invalid-incorrect-not-an-option 2>&1 | FileCheck %s --check-prefix=SANITY1
15+
; SANITY1: unknown argument: '-invalid-incorrect-not-an-option'
16+
17+
18+
;; Define a substitution used to simplify the testcases.
19+
; DEFINE: %{distributor} = dummy
20+
; DEFINE: %{extra_flags} = dummy
21+
; DEFINE: %{triple} = dummy
22+
; DEFINE: %{command} = llvm-lto2 run \
23+
; DEFINE: -thinlto-distributor-arg=%llvm_src_root/utils/dtlto/%{distributor} \
24+
; DEFINE: -thinlto-remote-opt-tool-arg=-Wunused-command-line-argument \
25+
; DEFINE: @%{triple}.rsp %{extra_flags}
26+
27+
28+
;; Write common arguments to a response files.
29+
30+
; RUN: echo "x86_64-unknown-linux-gnu.bc -o x86_64-unknown-linux-gnu.o \
31+
; RUN: -dtlto \
32+
; RUN: -dtlto-remote-opt-tool=%clang \
33+
; RUN: -thinlto-remote-opt-tool-arg=-Werror \
34+
; RUN: -dtlto-distributor=%python \
35+
; RUN: -r=x86_64-unknown-linux-gnu.bc,globalfunc1,plx" > x86_64-unknown-linux-gnu.rsp
36+
37+
; RUN: echo "x86_64-pc-windows-msvc.bc -o x86_64-pc-windows-msvc.o \
38+
; RUN: -dtlto \
39+
; RUN: -dtlto-remote-opt-tool=%clang \
40+
; RUN: -thinlto-remote-opt-tool-arg=-Werror \
41+
; RUN: -thinlto-remote-opt-tool-arg=-Wno-override-module \
42+
; RUN: -dtlto-distributor=%python \
43+
; RUN: -r=x86_64-pc-windows-msvc.bc,globalfunc2,plx" > x86_64-pc-windows-msvc.rsp
44+
45+
46+
;; Check that boolean configuration states are translated as expected and Clang
47+
;; accepts them.
48+
49+
; RUN: echo " \
50+
; RUN: --addrsig=1 \
51+
; RUN: -function-sections=1 \
52+
; RUN: -data-sections=1" > on.rsp
53+
54+
; RUN: echo " \
55+
; RUN: --addrsig=0 \
56+
; RUN: -function-sections=0 \
57+
; RUN: -data-sections=0" > off.rsp
58+
59+
;; Perform DTLTO with configuration state set.
60+
; REDEFINE: %{extra_flags} = @on.rsp
61+
; REDEFINE: %{distributor} = local.py
62+
; REDEFINE: %{triple} = x86_64-unknown-linux-gnu
63+
; RUN: %{command}
64+
; REDEFINE: %{distributor} = validate.py
65+
; RUN: not %{command} 2>&1 | FileCheck %s --check-prefix=ON \
66+
; RUN: --implicit-check-not=-no-pgo-warn-mismatch
67+
; ON-DAG: "-faddrsig"
68+
; ON-DAG: "-ffunction-sections"
69+
; ON-DAG: "-fdata-sections"
70+
71+
;; Perform DTLTO with configuration state unset.
72+
; REDEFINE: %{extra_flags} = @off.rsp
73+
; REDEFINE: %{distributor} = local.py
74+
; RUN: %{command}
75+
; REDEFINE: %{distributor} = validate.py
76+
; RUN: not %{command} 2>&1 | FileCheck %s --check-prefix=OFF
77+
; OFF-NOT: --implicit-check-not=--faddrsig
78+
; OFF-NOT: --implicit-check-not=--ffunction-sections
79+
; OFF-NOT: --implicit-check-not=--fdata-sections
80+
; OFF-NOT: --implicit-check-not=-no-pgo-warn-mismatch
81+
82+
83+
;; Check optimisation level.
84+
85+
; RUN: llvm-lto2 run \
86+
; RUN: -thinlto-distributor-arg=%llvm_src_root/utils/dtlto/local.py \
87+
; RUN: @x86_64-unknown-linux-gnu.rsp \
88+
; RUN: -O3
89+
90+
; RUN: not llvm-lto2 run \
91+
; RUN: -thinlto-distributor-arg=%llvm_src_root/utils/dtlto/validate.py \
92+
; RUN: @x86_64-unknown-linux-gnu.rsp \
93+
; RUN: -O3 2>&1 | FileCheck %s --check-prefix=OPTLEVEL
94+
; OPTLEVEL-DAG: "-O3"
95+
96+
97+
;; Check relocation model.
98+
99+
; REDEFINE: %{extra_flags} = -relocation-model=pic
100+
; REDEFINE: %{distributor} = local.py
101+
; RUN: %{command}
102+
; REDEFINE: %{distributor} = validate.py
103+
; RUN: not %{command} 2>&1 | FileCheck %s --check-prefix=PIC
104+
; PIC: -fpic
105+
106+
107+
; REDEFINE: %{extra_flags} = -relocation-model=pic
108+
; REDEFINE: %{distributor} = local.py
109+
; REDEFINE: %{triple} = x86_64-pc-windows-msvc
110+
; RUN: %{command}
111+
; REDEFINE: %{distributor} = validate.py
112+
; RUN: not %{command} 2>&1 | FileCheck %s --check-prefix=NOPIC
113+
; REDEFINE: %{triple} = x86_64-unknown-linux-gnu
114+
; NOPIC-NOT: -fpic
115+
116+
;; Check specifying a sample profile.
117+
; REDEFINE: %{extra_flags} = --lto-sample-profile-file="missing.profdata"
118+
; REDEFINE: %{distributor} = local.py
119+
; RUN: not %{command} 2>&1 | FileCheck %s --check-prefix=SAMPLE_PROFILE_ERR
120+
; SAMPLE_PROFILE_ERR: no such file or directory: 'missing.profdata'
121+
; REDEFINE: %{distributor} = validate.py
122+
; RUN: not %{command} 2>&1 | FileCheck %s --check-prefix=SAMPLE_PROFILE
123+
; SAMPLE_PROFILE-DAG: "-fprofile-sample-use=missing.profdata"
124+
125+
126+
;--- x86_64-unknown-linux-gnu.ll
127+
128+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
129+
target triple = "x86_64-unknown-linux-gnu"
130+
131+
define void @globalfunc1() {
132+
entry:
133+
ret void
134+
}
135+
136+
;--- x86_64-pc-windows-msvc.ll
137+
138+
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
139+
target triple = "x86_64-pc-windows-msvc"
140+
141+
define void @globalfunc2() {
142+
entry:
143+
ret void
144+
}

0 commit comments

Comments
 (0)