mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 08:45:46 +00:00
3608ab49b8
Some recent changes to how we set cross-language LTO for Windows resulted in compilation-time decreases and small performance regressions on a few benchmarks. The changes intended to remove explicit enablement of cross-language LTO for all builds, but rely on shippable builds being built with PGO and moz.configure's clever defaulting of cross-language LTO for PGO'd builds on Windows, which would then enable cross-language LTO for only shippable builds. Obviously something went wrong with those changes. The problem was our defaulting wasn't visible to moz.configure's logic for how to pass command-line options to the JS subconfigure. We set the value (`cross`) after the value for `--enable-lto` has been determined, and the default value is off (that is, `--disable-lto`). Since moz.configure is very thorough in passing configure options down into JS, it dutifully looked at what the default value of `--enable-lto` was supposed to be, and passed `--disable-lto` to JS's configure. There's some evidence that we knew our defaulting was a little sketchy: we'd only attempt cross-language LTO when we were performing the PGO use phase, and only if the value of `--enable-lto` wasn't explicitly passed. Which was a fine idea--you don't want to override what the user was trying to do--but in the case of JS backfired on us: the value *was* coming from the explicitly-passed command-line option of `--disable-lto`. So JS didn't enable any kind of LTO, with attendant consequences. This problem *didn't* happen before the aforementioned change because we were explicitly specifying that `--enable-lto=cross` should be passed in the mozconfig, which ensured that the correct setting was passed into JS. We were just setting `--enable-lto=cross` for *all* builds, which was less than desirable. The easiest way to fix all this is simply to put the `--enable-lto=cross` setting in the Windows mozconfigs, conditional on `MOZ_PGO_PROFILE_USE`. That placement captures the intent of the previous attempt at defaulting, but without the troubles described above: the option explicitly appears on the command line, and moz.configure will correctly pass it through to the JS subconfigure. This also makes our Windows configuration closer to our Linux configuration (the Linux configuration enables cross-language LTO for both PGO phases, which is arguably a bug). Differential Revision: https://phabricator.services.mozilla.com/D41080 --HG-- extra : moz-landing-system : lando
254 lines
9.2 KiB
Python
254 lines
9.2 KiB
Python
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
# vim: set filetype=python:
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
# PGO
|
|
# ==============================================================
|
|
@depends(c_compiler, check_build_environment, target)
|
|
@imports('multiprocessing')
|
|
@imports(_from='__builtin__', _import='min')
|
|
def pgo_flags(compiler, build_env, target):
|
|
topobjdir = build_env.topobjdir
|
|
if topobjdir.endswith('/js/src'):
|
|
topobjdir = topobjdir[:-7]
|
|
|
|
if compiler.type == 'gcc':
|
|
return namespace(
|
|
gen_cflags=['-fprofile-generate'],
|
|
gen_ldflags=['-fprofile-generate'],
|
|
use_cflags=['-fprofile-use', '-fprofile-correction',
|
|
'-Wcoverage-mismatch'],
|
|
use_ldflags=['-fprofile-use'],
|
|
)
|
|
|
|
if compiler.type in ('clang-cl', 'clang'):
|
|
profdata = os.path.join(topobjdir, 'merged.profdata')
|
|
prefix = ''
|
|
if compiler.type == 'clang-cl':
|
|
prefix = '/clang:'
|
|
if target.cpu == 'x86_64':
|
|
gen_ldflags = ['clang_rt.profile-x86_64.lib']
|
|
elif target.cpu == 'x86':
|
|
gen_ldflags = ['clang_rt.profile-i386.lib']
|
|
else:
|
|
gen_ldflags = None
|
|
else:
|
|
gen_ldflags = ['-fprofile-generate']
|
|
|
|
return namespace(
|
|
gen_cflags=[prefix + '-fprofile-generate'],
|
|
gen_ldflags=gen_ldflags,
|
|
use_cflags=[prefix + '-fprofile-use=%s' % profdata,
|
|
# Some error messages about mismatched profile data
|
|
# come in via -Wbackend-plugin, so disable those too.
|
|
'-Wno-error=backend-plugin'],
|
|
use_ldflags=[],
|
|
)
|
|
|
|
|
|
set_config('PROFILE_GEN_CFLAGS', pgo_flags.gen_cflags)
|
|
set_config('PROFILE_GEN_LDFLAGS', pgo_flags.gen_ldflags)
|
|
set_config('PROFILE_USE_CFLAGS', pgo_flags.use_cflags)
|
|
set_config('PROFILE_USE_LDFLAGS', pgo_flags.use_ldflags)
|
|
|
|
llvm_profdata = check_prog('LLVM_PROFDATA', ['llvm-profdata'],
|
|
allow_missing=True,
|
|
paths=toolchain_search_path)
|
|
|
|
js_option('--enable-profile-generate',
|
|
nargs='?',
|
|
choices=('cross',),
|
|
help='Build a PGO instrumented binary')
|
|
|
|
imply_option('MOZ_PGO',
|
|
depends_if('--enable-profile-generate')(lambda _: True))
|
|
|
|
set_config('MOZ_PROFILE_GENERATE',
|
|
depends_if('--enable-profile-generate')(lambda _: True))
|
|
|
|
set_define('MOZ_PROFILE_GENERATE',
|
|
depends_if('--enable-profile-generate')(lambda _: True))
|
|
|
|
js_option('--enable-profile-use',
|
|
nargs='?',
|
|
choices=('cross',),
|
|
help='Use a generated profile during the build')
|
|
|
|
js_option('--with-pgo-profile-path',
|
|
help='Path to the directory with unmerged profile data to use during the build',
|
|
nargs=1)
|
|
|
|
js_option('--enable-cross-pgo',
|
|
help='Enable PGO on Rust code')
|
|
|
|
imply_option('MOZ_PGO',
|
|
depends_if('--enable-profile-use')(lambda _: True))
|
|
|
|
set_config('MOZ_PROFILE_USE',
|
|
depends_if('--enable-profile-use')(lambda _: True))
|
|
|
|
|
|
@depends('--with-pgo-profile-path', '--enable-profile-use', llvm_profdata)
|
|
@imports('os')
|
|
def pgo_profile_path(path, pgo_use, profdata):
|
|
if not path:
|
|
return
|
|
if path and not pgo_use:
|
|
die('Pass --enable-profile-use to use --with-pgo-profile-path.')
|
|
if path and not profdata:
|
|
die('LLVM_PROFDATA must be set to process the pgo profile.')
|
|
if not os.path.isdir(path[0]):
|
|
die('Argument to --with-pgo-profile-path must be a directory.')
|
|
if not os.path.isabs(path[0]):
|
|
die('Argument to --with-pgo-profile-path must be an absolute path.')
|
|
return path[0]
|
|
|
|
|
|
set_config('PGO_PROFILE_PATH', pgo_profile_path)
|
|
|
|
option('--with-pgo-jarlog',
|
|
help='Use the provided jarlog file when packaging during a profile-use '
|
|
'build',
|
|
nargs=1)
|
|
|
|
set_config('PGO_JARLOG_PATH', depends_if('--with-pgo-jarlog')(lambda p: p))
|
|
|
|
|
|
@depends('MOZ_PGO', '--enable-profile-use', '--enable-profile-generate',
|
|
c_compiler, rustc_info)
|
|
def moz_pgo_rust(pgo, profile_use, profile_generate, c_compiler, rustc):
|
|
if not pgo:
|
|
return
|
|
|
|
# Enabling PGO through MOZ_PGO only and not --enable* flags.
|
|
if not profile_use and not profile_generate:
|
|
return
|
|
|
|
if profile_use and profile_generate:
|
|
die('Cannot build with --enable-profile-use and --enable-profile-generate.')
|
|
|
|
want_cross = (len(profile_use) and profile_use[0] == 'cross') \
|
|
or (len(profile_generate) and profile_generate[0] == 'cross')
|
|
|
|
if not want_cross:
|
|
return
|
|
|
|
if c_compiler.type == 'gcc':
|
|
die('Cannot use cross-language PGO with GCC.')
|
|
|
|
# PGO is not stable prior to 1.37
|
|
if rustc.version < Version('1.37'):
|
|
die('Cannot use cross-language PGO with Rust version %s.' % rustc.version)
|
|
|
|
return True
|
|
|
|
set_config('MOZ_PGO_RUST', moz_pgo_rust)
|
|
|
|
# LTO
|
|
# ==============================================================
|
|
|
|
js_option('--enable-lto',
|
|
env='MOZ_LTO',
|
|
nargs='?',
|
|
choices=('full', 'thin', 'cross'),
|
|
help='Enable LTO')
|
|
|
|
js_option(env='MOZ_LD64_KNOWN_GOOD',
|
|
nargs=1,
|
|
help='Indicate that ld64 is free of symbol aliasing bugs.')
|
|
|
|
imply_option('MOZ_LD64_KNOWN_GOOD', depends_if('MOZ_AUTOMATION')(lambda _: True))
|
|
|
|
@depends('--enable-lto', c_compiler, 'MOZ_LD64_KNOWN_GOOD', target)
|
|
@imports('multiprocessing')
|
|
def lto(value, c_compiler, ld64_known_good, target):
|
|
cflags = []
|
|
ldflags = []
|
|
enabled = None
|
|
rust_lto = False
|
|
|
|
if value:
|
|
enabled = True
|
|
# `cross` implies `thin`, but with Rust code participating in LTO
|
|
# as well. Make that a little more explicit.
|
|
if len(value) and value[0].lower() == 'cross':
|
|
if c_compiler.type == 'gcc':
|
|
die('Cross-language LTO is not supported with GCC.')
|
|
|
|
rust_lto = True
|
|
value = ['thin']
|
|
|
|
if target.kernel == 'Darwin' and target.os == 'OSX' \
|
|
and value[0].lower() == 'cross' and not ld64_known_good:
|
|
die('The Mac linker is known to have a bug that affects cross-language '
|
|
'LTO. If you know that your linker is free from this bug, please '
|
|
'set the environment variable `MOZ_LD64_KNOWN_GOOD=1` and re-run '
|
|
'configure.')
|
|
|
|
if c_compiler.type == 'clang':
|
|
if len(value) and value[0].lower() == 'full':
|
|
cflags.append("-flto")
|
|
ldflags.append("-flto")
|
|
else:
|
|
cflags.append("-flto=thin")
|
|
ldflags.append("-flto=thin")
|
|
elif c_compiler.type == 'clang-cl':
|
|
if len(value) and value[0].lower() == 'full':
|
|
cflags.append("-flto")
|
|
else:
|
|
cflags.append("-flto=thin")
|
|
# With clang-cl, -flto can only be used with -c or -fuse-ld=lld.
|
|
# AC_TRY_LINKs during configure don't have -c, so pass -fuse-ld=lld.
|
|
cflags.append("-fuse-ld=lld");
|
|
|
|
# Explicitly set the CPU to optimize for so the linker doesn't
|
|
# choose a poor default. Rust compilation by default uses the
|
|
# pentium4 CPU on x86:
|
|
#
|
|
# https://github.com/rust-lang/rust/blob/master/src/librustc_target/spec/i686_pc_windows_msvc.rs#L5
|
|
#
|
|
# which specifically supports "long" (multi-byte) nops. See
|
|
# https://bugzilla.mozilla.org/show_bug.cgi?id=1568450#c8 for details.
|
|
#
|
|
# The pentium4 seems like kind of a weird CPU to optimize for, but
|
|
# it seems to have worked out OK thus far. LLVM does not seem to
|
|
# specifically schedule code for the pentium4's deep pipeline, so
|
|
# that probably contributes to it being an OK default for our
|
|
# purposes.
|
|
if target.cpu == 'x86':
|
|
ldflags.append('-mllvm:-mcpu=pentium4')
|
|
# This is also the CPU that Rust uses. The LLVM source code
|
|
# recommends this as the "generic 64-bit specific x86 processor model":
|
|
#
|
|
# https://github.com/llvm/llvm-project/blob/e7694f34ab6a12b8bb480cbfcb396d0a64fe965f/llvm/lib/Target/X86/X86.td#L1165-L1187
|
|
if target.cpu == 'x86_64':
|
|
ldflags.append('-mllvm:-mcpu=x86-64')
|
|
# We do not need special flags for arm64. Hooray for fixed-length
|
|
# instruction sets.
|
|
else:
|
|
num_cores = multiprocessing.cpu_count()
|
|
cflags.append("-flto")
|
|
cflags.append("-flifetime-dse=1")
|
|
|
|
ldflags.append("-flto=%s" % num_cores)
|
|
ldflags.append("-flifetime-dse=1")
|
|
|
|
return namespace(
|
|
enabled=enabled,
|
|
cflags=cflags,
|
|
ldflags=ldflags,
|
|
rust_lto=rust_lto,
|
|
)
|
|
|
|
|
|
add_old_configure_assignment('MOZ_LTO', lto.enabled)
|
|
set_config('MOZ_LTO', lto.enabled)
|
|
set_define('MOZ_LTO', lto.enabled)
|
|
set_config('MOZ_LTO_CFLAGS', lto.cflags)
|
|
set_config('MOZ_LTO_LDFLAGS', lto.ldflags)
|
|
set_config('MOZ_LTO_RUST', lto.rust_lto)
|
|
add_old_configure_assignment('MOZ_LTO_CFLAGS', lto.cflags)
|
|
add_old_configure_assignment('MOZ_LTO_LDFLAGS', lto.ldflags)
|