Skip to content

Commit 7a7de41

Browse files
committed
tc-build: Introduce '--multicall'
LLVM introduced a multicall binary feature (similar to busybox) where all tools are linked into a single binary then symlinks to individual tools switch the behavior of the binary: $ ls -l clang llvm ld.lld lrwxrwxrwx - nathan 4 Feb 16:03 clang -> llvm lrwxrwxrwx - nathan 4 Feb 16:03 ld.lld -> llvm .rwxr-xr-x 288M nathan 4 Feb 16:03 llvm $ ./clang --version | head -1 ClangBuiltLinux clang version 23.0.0git (https://github.com/llvm/llvm-project.git dcf853df8fda885cafbc02f916ef01684b7d1104) $ ./llvm clang --version | head -1 ClangBuiltLinux clang version 23.0.0git (https://github.com/llvm/llvm-project.git dcf853df8fda885cafbc02f916ef01684b7d1104) $ ./ld.lld --version | head -1 ClangBuiltLinux LLD 23.0.0 (https://github.com/llvm/llvm-project.git dcf853df8fda885cafbc02f916ef01684b7d1104) (compatible with GNU linkers) $ ./llvm ld.lld --version | head -1 ClangBuiltLinux LLD 23.0.0 (https://github.com/llvm/llvm-project.git dcf853df8fda885cafbc02f916ef01684b7d1104) (compatible with GNU linkers) This combines the speed benefits of static linking with the space savings of dynamic linking. $ diskus pgo-bolt 632.09 MB (632,090,624 bytes) $ diskus multicall-pgo-bolt 321.14 MB (321,138,688 bytes) As this changes the layout of the installation, this may result in functional differences depending on software assumptions, so require the user to opt into this with a '--multicall' option. Adjust the BOLT process to use the correct binary. Signed-off-by: Nathan Chancellor <nathan@kernel.org>
1 parent aaec068 commit 7a7de41

File tree

2 files changed

+50
-16
lines changed

2 files changed

+50
-16
lines changed

build-llvm.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,14 @@
236236
'''),
237237
type=str,
238238
choices=['thin', 'full'])
239+
parser.add_argument('-m',
240+
'--multicall',
241+
help=textwrap.dedent('''\
242+
Build LLVM as a multicall binary via the LLVM_TOOL_LLVM_DRIVER_BUILD CMake option.
243+
This results in a much smaller installation on disk.
244+
245+
'''),
246+
action='store_true')
239247
parser.add_argument('-n',
240248
'--no-update',
241249
help=textwrap.dedent('''\
@@ -536,6 +544,8 @@
536544
if args.vendor_string:
537545
common_cmake_defines['CLANG_VENDOR'] = args.vendor_string
538546
common_cmake_defines['LLD_VENDOR'] = args.vendor_string
547+
if args.multicall:
548+
common_cmake_defines['LLVM_TOOL_LLVM_DRIVER_BUILD'] = 'ON'
539549
if args.defines:
540550
defines = dict(define.split('=', 1) for define in args.defines)
541551
common_cmake_defines.update(defines)

tc_build/llvm.py

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,26 +74,27 @@ def bolt_clang(self):
7474

7575
tc_build.utils.print_header(f"Performing BOLT with {mode}")
7676

77-
# clang-#: original binary
78-
# clang.bolt: BOLT optimized binary
77+
# real_binary: clang-# or llvm (if multicall is enabled)
78+
# bolt_binary: clang.bolt or llvm.bolt
7979
# .bolt will become original binary after optimization
80-
clang = Path(self.folders.build, 'bin/clang').resolve()
81-
clang_bolt = clang.with_name('clang.bolt')
80+
real_binary = Path(self.folders.build, 'bin/clang').resolve()
81+
binary_prefix = 'llvm' if self.multicall_is_enabled() else 'clang'
82+
bolt_binary = real_binary.with_name(f"{binary_prefix}.bolt")
8283

83-
bolt_profile = Path(self.folders.build, 'clang.fdata')
84+
bolt_profile = Path(self.folders.build, f"{binary_prefix}.fdata")
8485

8586
if mode == 'instrumentation':
86-
# clang.inst: instrumented binary, will be removed after generating profiles
87-
clang_inst = clang.with_name('clang.inst')
87+
# clang.inst / llvm.inst: instrumented binary, will be removed after generating profiles
88+
inst_binary = real_binary.with_name(f"{binary_prefix}.inst")
8889

8990
clang_inst_cmd = [
9091
self.tools.llvm_bolt,
9192
'--instrument',
9293
f"--instrumentation-file={bolt_profile}",
9394
'--instrumentation-file-append-pid',
9495
'-o',
95-
clang_inst,
96-
clang,
96+
inst_binary,
97+
real_binary,
9798
]
9899
# When running an instrumented binary on certain platforms (namely
99100
# Apple Silicon), there may be hangs due to instrumentation in
@@ -104,7 +105,16 @@ def bolt_clang(self):
104105
clang_inst_cmd.append('--conservative-instrumentation')
105106
self.run_cmd(clang_inst_cmd)
106107

107-
self.bolt_builder.bolt_instrumentation = True
108+
if binary_prefix == 'llvm':
109+
# The multicall tools are all symlinked to the 'llvm' binary.
110+
# To avoid having to mess with those symlinks...
111+
orig_binary = real_binary.with_name('llvm.orig')
112+
real_binary.replace(orig_binary) # mv llvm llvm.orig
113+
inst_binary.replace(real_binary) # mv llvm.inst llvm
114+
else:
115+
# This option changes CC when building Linux, which is only
116+
# needed when just clang is instrumented.
117+
self.bolt_builder.bolt_instrumentation = True
108118

109119
if mode == 'sampling':
110120
self.bolt_builder.bolt_sampling_output = Path(self.folders.build, 'perf.data')
@@ -115,6 +125,11 @@ def bolt_clang(self):
115125
# With instrumentation, we need to combine the profiles we generated,
116126
# as they are separated by PID
117127
if mode == 'instrumentation':
128+
# Undo shuffle from above
129+
if binary_prefix == 'llvm':
130+
real_binary.replace(inst_binary) # mv llvm llvm.inst
131+
orig_binary.replace(real_binary) # mv llvm.orig llvm
132+
118133
fdata_files = bolt_profile.parent.glob(f"{bolt_profile.name}.*.fdata")
119134

120135
# merge-fdata will print one line for each .fdata it merges.
@@ -139,7 +154,7 @@ def bolt_clang(self):
139154
self.bolt_builder.bolt_sampling_output,
140155
'-o',
141156
bolt_profile,
142-
clang,
157+
real_binary,
143158
]
144159
self.run_cmd(perf2bolt_cmd)
145160
self.bolt_builder.bolt_sampling_output.unlink()
@@ -166,18 +181,18 @@ def bolt_clang(self):
166181
'--dyno-stats',
167182
f"--icf={icf_val}",
168183
'-o',
169-
clang_bolt,
184+
bolt_binary,
170185
f"--reorder-blocks={'cache+' if use_cache_plus else 'ext-tsp'}",
171186
f"--reorder-functions={reorder_funcs_val}",
172187
'--split-all-cold',
173188
f"--split-functions{'=3' if use_sf_val else ''}",
174189
'--use-gnu-stack',
175-
clang,
190+
real_binary,
176191
]
177192
self.run_cmd(clang_opt_cmd)
178-
clang_bolt.replace(clang)
193+
bolt_binary.replace(real_binary)
179194
if mode == 'instrumentation':
180-
clang_inst.unlink()
195+
inst_binary.unlink()
181196

182197
def build(self):
183198
if not self.folders.build:
@@ -388,6 +403,9 @@ def host_target(self):
388403
def host_target_is_enabled(self):
389404
return 'all' in self.targets or self.host_target() in self.targets
390405

406+
def multicall_is_enabled(self):
407+
return self.cmake_defines.get('LLVM_TOOL_LLVM_DRIVER_BUILD', 'OFF') == 'ON'
408+
391409
def project_is_enabled(self, project):
392410
return 'all' in self.projects or project in self.projects
393411

@@ -573,7 +591,6 @@ def __init__(self):
573591

574592
self.cmake_defines['LLVM_BUILD_INSTRUMENTED'] = 'IR'
575593
self.cmake_defines['LLVM_BUILD_RUNTIME'] = 'OFF'
576-
self.cmake_defines['LLVM_LINK_LLVM_DYLIB'] = 'ON'
577594

578595
def configure(self):
579596
# The following defines are needed to avoid thousands of warnings
@@ -605,6 +622,13 @@ def configure(self):
605622
self.cmake_defines['CMAKE_C_FLAGS'] = ' '.join(cflags)
606623
self.cmake_defines['CMAKE_CXX_FLAGS'] = ' '.join(cxxflags)
607624

625+
# These are currently incompatible:
626+
# https://github.com/llvm/llvm-project/pull/133596
627+
# But that should not matter much in this case because multicall uses
628+
# much less disk space.
629+
if not self.multicall_is_enabled():
630+
self.cmake_defines['LLVM_LINK_LLVM_DYLIB'] = 'ON'
631+
608632
super().configure()
609633

610634
def generate_profdata(self):

0 commit comments

Comments
 (0)