@@ -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,18 @@ 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, perform a
111+ # shuffle of the real binary with the instrumented binary for
112+ # the build process.
113+ orig_binary = real_binary .with_name ('llvm.orig' )
114+ real_binary .replace (orig_binary ) # mv llvm llvm.orig
115+ inst_binary .replace (real_binary ) # mv llvm.inst llvm
116+ else :
117+ # This option changes CC when building Linux, which is only
118+ # needed when just clang is instrumented.
119+ self .bolt_builder .bolt_instrumentation = True
108120
109121 if mode == 'sampling' :
110122 self .bolt_builder .bolt_sampling_output = Path (self .folders .build , 'perf.data' )
@@ -115,6 +127,11 @@ def bolt_clang(self):
115127 # With instrumentation, we need to combine the profiles we generated,
116128 # as they are separated by PID
117129 if mode == 'instrumentation' :
130+ # Undo shuffle from above
131+ if binary_prefix == 'llvm' :
132+ real_binary .replace (inst_binary ) # mv llvm llvm.inst
133+ orig_binary .replace (real_binary ) # mv llvm.orig llvm
134+
118135 fdata_files = bolt_profile .parent .glob (f"{ bolt_profile .name } .*.fdata" )
119136
120137 # merge-fdata will print one line for each .fdata it merges.
@@ -139,7 +156,7 @@ def bolt_clang(self):
139156 self .bolt_builder .bolt_sampling_output ,
140157 '-o' ,
141158 bolt_profile ,
142- clang ,
159+ real_binary ,
143160 ]
144161 self .run_cmd (perf2bolt_cmd )
145162 self .bolt_builder .bolt_sampling_output .unlink ()
@@ -166,18 +183,18 @@ def bolt_clang(self):
166183 '--dyno-stats' ,
167184 f"--icf={ icf_val } " ,
168185 '-o' ,
169- clang_bolt ,
186+ bolt_binary ,
170187 f"--reorder-blocks={ 'cache+' if use_cache_plus else 'ext-tsp' } " ,
171188 f"--reorder-functions={ reorder_funcs_val } " ,
172189 '--split-all-cold' ,
173190 f"--split-functions{ '=3' if use_sf_val else '' } " ,
174191 '--use-gnu-stack' ,
175- clang ,
192+ real_binary ,
176193 ]
177194 self .run_cmd (clang_opt_cmd )
178- clang_bolt .replace (clang )
195+ bolt_binary .replace (real_binary )
179196 if mode == 'instrumentation' :
180- clang_inst .unlink ()
197+ inst_binary .unlink ()
181198
182199 def build (self ):
183200 if not self .folders .build :
@@ -388,6 +405,26 @@ def host_target(self):
388405 def host_target_is_enabled (self ):
389406 return 'all' in self .targets or self .host_target () in self .targets
390407
408+ def llvm_driver_binaries (self , project ):
409+ # Find all CMakeLists.txt for LLVM or clang tools that have multicall driver support
410+ cmakelists_txts = [
411+ cmakelists_txt
412+ for path in Path (self .folders .source , project ).glob ('tools/*/CMakeLists.txt' )
413+ if ' GENERATE_DRIVER' in (cmakelists_txt := path .read_text (encoding = 'utf-8' ))
414+ ]
415+ skip_tools = (
416+ # llvm-mt depends on libxml2, which we explicitly do not link against
417+ 'llvm-mt' , )
418+ # Return the values of the add_clang_tool() or add_llvm_tool() CMake macros
419+ return [
420+ tool for cmakelists_txt in cmakelists_txts
421+ if (match := re .search (r"^add_(?:clang|llvm)_tool\((.*)$" , cmakelists_txt , flags = re .M ))
422+ and (tool := match .groups ()[0 ]) not in skip_tools
423+ ]
424+
425+ def multicall_is_enabled (self ):
426+ return self .cmake_defines .get ('LLVM_TOOL_LLVM_DRIVER_BUILD' , 'OFF' ) == 'ON'
427+
391428 def project_is_enabled (self , project ):
392429 return 'all' in self .projects or project in self .projects
393430
@@ -495,10 +532,22 @@ def configure(self):
495532 'llvm-readelf' ,
496533 'llvm-strip' ,
497534 ]
535+ # If multicall is enabled, we need to add all possible tools to the
536+ # distribution components list to prevent them from being built as
537+ # standalone tools, which may break the build for tools like
538+ # llvm-symbolizer because they need LLVMDebuginfod but it is not
539+ # linked in that configuration. While this does build a little more
540+ # code for the 'distribution' target, it should result in only a
541+ # slight increase in installation size due to being a multicall
542+ # binary.
543+ if self .multicall_is_enabled ():
544+ distribution_components += [item for item in self .llvm_driver_binaries ('llvm' ) if item not in distribution_components ]
498545 if self .project_is_enabled ('bolt' ):
499546 distribution_components .append ('bolt' )
500547 if self .project_is_enabled ('clang' ):
501548 distribution_components += ['clang' , 'clang-resource-headers' ]
549+ if self .multicall_is_enabled ():
550+ distribution_components += [item for item in self .llvm_driver_binaries ('clang' ) if item not in distribution_components ]
502551 if self .project_is_enabled ('lld' ):
503552 distribution_components .append ('lld' )
504553 if build_compiler_rt :
@@ -573,16 +622,18 @@ def __init__(self):
573622
574623 self .cmake_defines ['LLVM_BUILD_INSTRUMENTED' ] = 'IR'
575624 self .cmake_defines ['LLVM_BUILD_RUNTIME' ] = 'OFF'
576- self .cmake_defines ['LLVM_LINK_LLVM_DYLIB' ] = 'ON'
577625
578626 def configure (self ):
627+ no_multicall = not self .multicall_is_enabled ()
579628 # The following defines are needed to avoid thousands of warnings
580629 # along the lines of:
581630 # "Unable to track new values: Running out of static counters."
582- # They require LLVM_LINK_DYLIB to be enabled, which is done above.
631+ # LLVM_VP_COUNTERS_PER_SITE requires LLVM_LINK_DYLIB, which is only
632+ # done when multicall is not enabled. If multicall is enabled, we need
633+ # to use CMAKE_C{,XX}_FLAGS.
583634 cmake_options = Path (self .folders .source , 'llvm/cmake/modules/HandleLLVMOptions.cmake' )
584635 cmake_text = cmake_options .read_text (encoding = 'utf-8' )
585- if 'LLVM_VP_COUNTERS_PER_SITE' in cmake_text :
636+ if no_multicall and 'LLVM_VP_COUNTERS_PER_SITE' in cmake_text :
586637 self .cmake_defines ['LLVM_VP_COUNTERS_PER_SITE' ] = '6'
587638 else :
588639 cflags = []
@@ -605,6 +656,13 @@ def configure(self):
605656 self .cmake_defines ['CMAKE_C_FLAGS' ] = ' ' .join (cflags )
606657 self .cmake_defines ['CMAKE_CXX_FLAGS' ] = ' ' .join (cxxflags )
607658
659+ # These are currently incompatible:
660+ # https://github.com/llvm/llvm-project/pull/133596
661+ # But that should not matter much in this case because multicall uses
662+ # much less disk space.
663+ if no_multicall :
664+ self .cmake_defines ['LLVM_LINK_LLVM_DYLIB' ] = 'ON'
665+
608666 super ().configure ()
609667
610668 def generate_profdata (self ):
0 commit comments