Bug 1914053 - Adjust width of HNT search bar r=home-newtab-reviewers,nbarrett
[gecko.git] / build / moz.configure / lto-pgo.configure
blob603af6e1d22beef38fa0008839ad46bf1f07a997
1 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
2 # vim: set filetype=python:
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 # PGO
8 # ==============================================================
9 llvm_profdata = check_prog(
10     "LLVM_PROFDATA", ["llvm-profdata"], allow_missing=True, paths=clang_search_path
14 @depends_if(llvm_profdata)
15 @checking("whether llvm-profdata supports 'order' subcommand")
16 def llvm_profdata_order(profdata):
17     retcode, _, _ = get_cmd_output(profdata, "order", "--help")
18     return retcode == 0
21 option(
22     "--enable-profile-generate",
23     env="MOZ_PROFILE_GENERATE",
24     nargs="?",
25     choices=("cross",),
26     help="Build a PGO instrumented binary",
29 enable_profile_generate = depends_if("--enable-profile-generate")(lambda _: True)
32 imply_option("MOZ_PGO", enable_profile_generate)
34 set_config("MOZ_PROFILE_GENERATE", enable_profile_generate)
36 set_define("MOZ_PROFILE_GENERATE", enable_profile_generate)
38 option(
39     "--enable-profile-use",
40     env="MOZ_PROFILE_USE",
41     nargs="?",
42     choices=("cross",),
43     help="Use a generated profile during the build",
45 enable_profile_use = depends_if("--enable-profile-use")(lambda _: True)
47 imply_option("MOZ_PGO", enable_profile_use)
48 set_config("MOZ_PROFILE_USE", enable_profile_use)
51 option(
52     "--with-pgo-profile-path",
53     help="Path to the directory with unmerged profile data to use during the build"
54     ", or to a merged profdata file",
55     nargs=1,
59 @depends(
60     "--with-pgo-profile-path",
61     "--enable-profile-use",
62     llvm_profdata,
63     build_environment,
65 @imports("os")
66 def pgo_profile_path(path, pgo_use, profdata, build_env):
67     topobjdir = build_env.topobjdir
68     if topobjdir.endswith("/js/src"):
69         topobjdir = topobjdir[:-7]
71     if not path:
72         return os.path.join(topobjdir, "instrumented", "merged.profdata")
73     if path and not pgo_use:
74         die("Pass --enable-profile-use to use --with-pgo-profile-path.")
75     if path and not profdata:
76         die("LLVM_PROFDATA must be set to process the pgo profile.")
77     if not os.path.isfile(path[0]):
78         die("Argument to --with-pgo-profile-path must be a file.")
79     if not os.path.isabs(path[0]):
80         die("Argument to --with-pgo-profile-path must be an absolute path.")
81     return path[0]
84 set_config("PGO_PROFILE_PATH", pgo_profile_path)
87 @depends(
88     "--enable-profile-use",
89     pgo_profile_path,
90     llvm_profdata,
91     llvm_profdata_order,
92     build_environment,
94 def orderfile_path(profile_use, path, profdata, profdata_order, build_env):
95     if not profile_use:
96         return None
98     if not profdata_order:
99         return None
101     topobjdir = build_env.topobjdir
103     orderfile = os.path.join(topobjdir, "orderfile.txt")
104     check_cmd_output(profdata, "order", path, "-o", orderfile)
105     return orderfile
108 pgo_temporal = c_compiler.try_compile(
109     flags=["-fprofile-generate", "-mllvm", "-pgo-temporal-instrumentation"],
110     check_msg="whether the C compiler supports temporal instrumentation",
111     when="--enable-profile-generate",
115 @depends(
116     c_compiler,
117     select_linker,
118     target,
119     pgo_profile_path,
120     orderfile_path,
121     target_is_windows,
122     pgo_temporal,
124 @imports("multiprocessing")
125 def pgo_flags(
126     compiler, linker, target, profdata, orderfile, target_is_windows, pgo_temporal
128     if compiler.type == "gcc":
129         return namespace(
130             gen_cflags=["-fprofile-generate"],
131             gen_ldflags=["-fprofile-generate"],
132             use_cflags=["-fprofile-use", "-fprofile-correction", "-Wcoverage-mismatch"],
133             use_ldflags=["-fprofile-use"],
134         )
136     if compiler.type in ("clang-cl", "clang"):
137         prefix = ""
138         if compiler.type == "clang-cl":
139             prefix = "/clang:"
140             gen_ldflags = None
141         else:
142             gen_ldflags = ["-fprofile-generate"]
144         use_ldflags = []
145         if orderfile:
146             if compiler.type == "clang-cl":
147                 use_ldflags += [
148                     "-ORDER:@" + orderfile,
149                     "/ignore:4037",  # Disable warn missing order symbol
150                 ]
151             elif linker.KIND == "ld64" or (linker.KIND == "lld" and target.os == "OSX"):
152                 use_ldflags += ["-Wl,-order_file", orderfile]
153             elif linker.KIND == "lld":
154                 use_ldflags += [
155                     "-Wl,--symbol-ordering-file",
156                     orderfile,
157                     "-Wl,--no-warn-symbol-ordering",
158                 ]
160         if use_ldflags:
161             log.info("Activating PGO-based orderfile")
163         gen_cflags = [prefix + "-fprofile-generate"]
165         if pgo_temporal:
166             gen_cflags += ["-mllvm", "-pgo-temporal-instrumentation"]
168         if target_is_windows:
169             # native llvm-profdata.exe on Windows can't read profile data
170             # if name compression is enabled (which cross-compiling enables
171             # by default)
172             gen_cflags += ["-mllvm", "-enable-name-compression=false"]
174         return namespace(
175             gen_cflags=gen_cflags,
176             gen_ldflags=gen_ldflags,
177             use_cflags=[
178                 prefix + "-fprofile-use=%s" % profdata,
179                 # Some error messages about mismatched profile data
180                 # come in via -Wbackend-plugin, so disable those too.
181                 "-Wno-error=backend-plugin",
182             ],
183             use_ldflags=use_ldflags,
184         )
187 set_config("PROFILE_GEN_CFLAGS", pgo_flags.gen_cflags)
188 set_config("PROFILE_GEN_LDFLAGS", pgo_flags.gen_ldflags)
189 set_config("PROFILE_USE_CFLAGS", pgo_flags.use_cflags)
190 set_config("PROFILE_USE_LDFLAGS", pgo_flags.use_ldflags)
192 option(
193     "--with-pgo-jarlog",
194     help="Use the provided jarlog file when packaging during a profile-use " "build",
195     nargs=1,
198 set_config("PGO_JARLOG_PATH", depends_if("--with-pgo-jarlog")(lambda p: p))
201 @depends("MOZ_PGO", "--enable-profile-use", "--enable-profile-generate", c_compiler)
202 def moz_pgo_rust(pgo, profile_use, profile_generate, c_compiler):
203     if not pgo:
204         return
206     # Enabling PGO through MOZ_PGO only and not --enable* flags.
207     if not profile_use and not profile_generate:
208         return
210     if profile_use and profile_generate:
211         die("Cannot build with --enable-profile-use and --enable-profile-generate.")
213     want_cross = (len(profile_use) and profile_use[0] == "cross") or (
214         len(profile_generate) and profile_generate[0] == "cross"
215     )
217     if not want_cross:
218         return
220     if c_compiler.type == "gcc":
221         die("Cannot use cross-language PGO with GCC.")
223     return True
226 set_config("MOZ_PGO_RUST", moz_pgo_rust)
228 # LTO
229 # ==============================================================
231 option(
232     "--enable-lto",
233     env="MOZ_LTO",
234     nargs="*",
235     choices=("full", "thin", "cross"),
236     help="Enable LTO",
239 option(
240     env="MOZ_LD64_KNOWN_GOOD",
241     nargs=1,
242     help="Indicate that ld64 is free of symbol aliasing bugs.",
245 imply_option("MOZ_LD64_KNOWN_GOOD", depends_if("MOZ_AUTOMATION")(lambda _: True))
248 @depends(
249     "--enable-lto",
250     c_compiler,
251     select_linker,
252     "MOZ_LD64_KNOWN_GOOD",
253     target,
254     "--enable-profile-generate",
255     pass_manager.enabled,
256     "--enable-profile-use",
257     "MOZ_AUTOMATION",
259 @imports("multiprocessing")
260 def lto(
261     values,
262     c_compiler,
263     select_linker,
264     ld64_known_good,
265     target,
266     instrumented_build,
267     pass_manager,
268     pgo_build,
269     moz_automation,
271     cflags = []
272     ldflags = []
273     enabled = None
274     rust_lto = False
276     if not values:
277         return
279     # Sanitize LTO modes.
280     if "full" in values and "thin" in values:
281         die("incompatible --enable-lto choices 'full' and 'thin'")
283     # If a value was given to --enable-lto, use that.  Otherwise, make the lto
284     # mode explicit, using full with gcc, and full or thin with clang depending
285     # on the performance benefit.
286     # Defaulting to full LTO is costly in terms of compilation time, so we only
287     # default to it if MOZ_AUTOMATION and PGO are on, and for some platforms.
288     # Based on speedometer3 scores, full lto + pgo is beneficial for Linux and
289     # Windows for x86_64 targets.
290     if values == () or values == ("cross",):
291         if c_compiler.type == "gcc":
292             values += ("full",)
293         elif (
294             pgo_build
295             and moz_automation
296             and target.os in ("WINNT", "GNU")
297             and target.cpu == "x86_64"
298         ):
299             values += ("full",)
300         else:
301             values += ("thin",)
303     if instrumented_build:
304         log.warning("Disabling LTO because --enable-profile-generate is specified")
305         return
307     if c_compiler.type == "gcc":
308         if "cross" in values:
309             die("Cross-language LTO is not supported with GCC.")
310         if "thin" in values:
311             die(
312                 "gcc does not support thin LTO. Use `--enable-lto` "
313                 "to enable full LTO for gcc."
314             )
316     if (
317         target.kernel == "Darwin"
318         and "cross" in values
319         and select_linker.KIND == "ld64"
320         and not ld64_known_good
321     ):
322         die(
323             "The Mac linker is known to have a bug that affects cross-language "
324             "LTO.  If you know that your linker is free from this bug, please "
325             "set the environment variable `MOZ_LD64_KNOWN_GOOD=1` and re-run "
326             "configure."
327         )
329     if c_compiler.type == "clang":
330         if "full" in values:
331             cflags.append("-flto")
332             ldflags.append("-flto")
333         else:
334             cflags.append("-flto=thin")
335             ldflags.append("-flto=thin")
337         if target.os == "Android" and "cross" in values:
338             # Work around https://github.com/rust-lang/rust/issues/90088
339             # by enabling the highest level of SSE the rust targets default
340             # to.
341             # https://github.com/rust-lang/rust/blob/bdfcb88e8b6203ccb46a2fb6649979b773efc8ac/compiler/rustc_target/src/spec/i686_linux_android.rs#L13
342             # https://github.com/rust-lang/rust/blob/8d1083e319841624f64400e1524805a40d725439/compiler/rustc_target/src/spec/x86_64_linux_android.rs#L7
343             if target.cpu == "x86":
344                 ldflags.append("-Wl,-plugin-opt=-mattr=+ssse3")
345             elif target.cpu == "x86_64":
346                 ldflags.append("-Wl,-plugin-opt=-mattr=+sse4.2")
347     elif c_compiler.type == "clang-cl":
348         if "full" in values:
349             cflags.append("-flto")
350         else:
351             cflags.append("-flto=thin")
352         # With clang-cl, -flto can only be used with -c or -fuse-ld=lld.
353         # AC_TRY_LINKs during configure don't have -c, so pass -fuse-ld=lld.
354         cflags.append("-fuse-ld=lld")
356         # Explicitly set the CPU to optimize for so the linker doesn't
357         # choose a poor default.  Rust compilation by default uses the
358         # pentium4 CPU on x86:
359         #
360         # https://github.com/rust-lang/rust/blob/049a49b91151a88c95fa0d62a53fd0a0ac2c3af9/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs#L5
361         #
362         # which specifically supports "long" (multi-byte) nops.  See
363         # https://bugzilla.mozilla.org/show_bug.cgi?id=1568450#c8 for details.
364         #
365         # The pentium4 seems like kind of a weird CPU to optimize for, but
366         # it seems to have worked out OK thus far.  LLVM does not seem to
367         # specifically schedule code for the pentium4's deep pipeline, so
368         # that probably contributes to it being an OK default for our
369         # purposes.
370         if target.cpu == "x86":
371             ldflags.append("-mllvm:-mcpu=pentium4")
372         # This is also the CPU that Rust uses.  The LLVM source code
373         # recommends this as the "generic 64-bit specific x86 processor model":
374         #
375         # https://github.com/llvm/llvm-project/blob/e7694f34ab6a12b8bb480cbfcb396d0a64fe965f/llvm/lib/Target/X86/X86.td#L1165-L1187
376         if target.cpu == "x86_64":
377             ldflags.append("-mllvm:-mcpu=x86-64")
378         # We do not need special flags for arm64.  Hooray for fixed-length
379         # instruction sets.
380     else:
381         num_cores = multiprocessing.cpu_count()
382         cflags.append("-flto")
383         cflags.append("-flifetime-dse=1")
385         ldflags.append("-flto=%s" % num_cores)
386         ldflags.append("-flifetime-dse=1")
388     # Tell LTO not to inline functions above a certain size, to mitigate
389     # binary size growth while still getting good performance.
390     # (For hot functions, PGO will put a multiplier on this limit.)
391     if target.os == "WINNT":
392         ldflags.append("-mllvm:-import-instr-limit=10")
393     elif target.kernel == "Darwin":
394         ldflags.append("-Wl,-mllvm,-import-instr-limit=10")
395     elif c_compiler.type == "clang":
396         ldflags.append("-Wl,-plugin-opt=-import-instr-limit=10")
398     # If we're using the new pass manager, we can also enable the new PM
399     # during LTO. Further we can use the resulting size savings to increase
400     # the import limit in hot functions.
401     if pass_manager:
402         if target.os == "WINNT":
403             if c_compiler.version >= "12.0.0" and c_compiler.version < "13.0.0":
404                 ldflags.append("-opt:ltonewpassmanager")
405             if c_compiler.version >= "12.0.0":
406                 ldflags.append("-mllvm:-import-hot-multiplier=30")
407         elif target.kernel == "Darwin":
408             ldflags.append("-Wl,-mllvm,-import-hot-multiplier=30")
409         else:
410             if c_compiler.version < "13.0.0":
411                 ldflags.append("-Wl,-plugin-opt=new-pass-manager")
412             ldflags.append("-Wl,-plugin-opt=-import-hot-multiplier=30")
414     # Pick Rust LTO mode in case of cross lTO. Thin is the default.
415     if "cross" in values:
416         rust_lto = "full" if "full" in values else "thin"
417     else:
418         rust_lto = ""
420     return namespace(
421         enabled=True,
422         cflags=cflags,
423         ldflags=ldflags,
424         rust_lto=rust_lto,
425     )
428 @depends(
429     dso_flags,
430     when=building_with_gnu_compatible_cc
431     & gcc_use_gnu_ld
432     & ~developer_options
433     & ~enable_profile_generate,
435 def remove_dead_symbols(dso_flags):
436     dso_flags.ldopts.append("-Wl,--gc-sections")
439 add_old_configure_assignment("MOZ_LTO", lto.enabled)
440 set_config("MOZ_LTO", lto.enabled)
441 set_define("MOZ_LTO", lto.enabled)
442 set_config("MOZ_LTO_CFLAGS", lto.cflags)
443 set_config("MOZ_LTO_LDFLAGS", lto.ldflags)
444 set_config("MOZ_LTO_RUST_CROSS", lto.rust_lto)
445 add_old_configure_assignment("MOZ_LTO_CFLAGS", lto.cflags)
446 add_old_configure_assignment("MOZ_LTO_LDFLAGS", lto.ldflags)