Bug 1885580 - Add a MenuGroup component for the menu redesign r=android-reviewers,007
[gecko.git] / build / moz.configure / lto-pgo.configure
blob5048b367e2686169f7d3f4644b7f629d1553004c
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 imply_option("MOZ_PGO", depends_if("--enable-profile-generate")(lambda _: True))
31 set_config(
32     "MOZ_PROFILE_GENERATE", depends_if("--enable-profile-generate")(lambda _: True)
35 set_define(
36     "MOZ_PROFILE_GENERATE", depends_if("--enable-profile-generate")(lambda _: True)
39 add_old_configure_assignment(
40     "MOZ_PROFILE_GENERATE", 1, when="--enable-profile-generate"
43 option(
44     "--enable-profile-use",
45     env="MOZ_PROFILE_USE",
46     nargs="?",
47     choices=("cross",),
48     help="Use a generated profile during the build",
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,
58 imply_option("MOZ_PGO", depends_if("--enable-profile-use")(lambda _: True))
60 set_config("MOZ_PROFILE_USE", depends_if("--enable-profile-use")(lambda _: True))
63 @depends(
64     "--with-pgo-profile-path",
65     "--enable-profile-use",
66     llvm_profdata,
67     build_environment,
69 @imports("os")
70 def pgo_profile_path(path, pgo_use, profdata, build_env):
71     topobjdir = build_env.topobjdir
72     if topobjdir.endswith("/js/src"):
73         topobjdir = topobjdir[:-7]
75     if not path:
76         return os.path.join(topobjdir, "instrumented", "merged.profdata")
77     if path and not pgo_use:
78         die("Pass --enable-profile-use to use --with-pgo-profile-path.")
79     if path and not profdata:
80         die("LLVM_PROFDATA must be set to process the pgo profile.")
81     if not os.path.isfile(path[0]):
82         die("Argument to --with-pgo-profile-path must be a file.")
83     if not os.path.isabs(path[0]):
84         die("Argument to --with-pgo-profile-path must be an absolute path.")
85     return path[0]
88 set_config("PGO_PROFILE_PATH", pgo_profile_path)
91 @depends(
92     "--enable-profile-use",
93     pgo_profile_path,
94     llvm_profdata,
95     llvm_profdata_order,
96     build_environment,
98 def orderfile_path(profile_use, path, profdata, profdata_order, build_env):
99     if not profile_use:
100         return None
102     if not profdata_order:
103         return None
105     topobjdir = build_env.topobjdir
107     orderfile = os.path.join(topobjdir, "orderfile.txt")
108     check_cmd_output(profdata, "order", path, "-o", orderfile)
109     return orderfile
112 pgo_temporal = c_compiler.try_compile(
113     flags=["-fprofile-generate", "-mllvm", "-pgo-temporal-instrumentation"],
114     check_msg="whether the C compiler supports temporal instrumentation",
115     when="--enable-profile-generate",
119 @depends(
120     c_compiler,
121     select_linker,
122     target,
123     pgo_profile_path,
124     orderfile_path,
125     target_is_windows,
126     pgo_temporal,
128 @imports("multiprocessing")
129 def pgo_flags(
130     compiler, linker, target, profdata, orderfile, target_is_windows, pgo_temporal
132     if compiler.type == "gcc":
133         return namespace(
134             gen_cflags=["-fprofile-generate"],
135             gen_ldflags=["-fprofile-generate"],
136             use_cflags=["-fprofile-use", "-fprofile-correction", "-Wcoverage-mismatch"],
137             use_ldflags=["-fprofile-use"],
138         )
140     if compiler.type in ("clang-cl", "clang"):
141         prefix = ""
142         if compiler.type == "clang-cl":
143             prefix = "/clang:"
144             gen_ldflags = None
145         else:
146             gen_ldflags = ["-fprofile-generate"]
148         use_ldflags = []
149         if orderfile:
150             if compiler.type == "clang-cl":
151                 use_ldflags += [
152                     "-ORDER:@" + orderfile,
153                     "/ignore:4037",  # Disable warn missing order symbol
154                 ]
155             elif linker.KIND == "ld64" or (linker.KIND == "lld" and target.os == "OSX"):
156                 use_ldflags += ["-Wl,-order_file", orderfile]
157             elif linker.KIND == "lld":
158                 use_ldflags += [
159                     "-Wl,--symbol-ordering-file",
160                     orderfile,
161                     "-Wl,--no-warn-symbol-ordering",
162                 ]
164         if use_ldflags:
165             log.info("Activating PGO-based orderfile")
167         gen_cflags = [prefix + "-fprofile-generate"]
169         if pgo_temporal:
170             gen_cflags += ["-mllvm", "-pgo-temporal-instrumentation"]
172         if target_is_windows:
173             # native llvm-profdata.exe on Windows can't read profile data
174             # if name compression is enabled (which cross-compiling enables
175             # by default)
176             gen_cflags += ["-mllvm", "-enable-name-compression=false"]
178         return namespace(
179             gen_cflags=gen_cflags,
180             gen_ldflags=gen_ldflags,
181             use_cflags=[
182                 prefix + "-fprofile-use=%s" % profdata,
183                 # Some error messages about mismatched profile data
184                 # come in via -Wbackend-plugin, so disable those too.
185                 "-Wno-error=backend-plugin",
186             ],
187             use_ldflags=use_ldflags,
188         )
191 set_config("PROFILE_GEN_CFLAGS", pgo_flags.gen_cflags)
192 set_config("PROFILE_GEN_LDFLAGS", pgo_flags.gen_ldflags)
193 set_config("PROFILE_USE_CFLAGS", pgo_flags.use_cflags)
194 set_config("PROFILE_USE_LDFLAGS", pgo_flags.use_ldflags)
196 option(
197     "--with-pgo-jarlog",
198     help="Use the provided jarlog file when packaging during a profile-use " "build",
199     nargs=1,
202 set_config("PGO_JARLOG_PATH", depends_if("--with-pgo-jarlog")(lambda p: p))
205 @depends("MOZ_PGO", "--enable-profile-use", "--enable-profile-generate", c_compiler)
206 def moz_pgo_rust(pgo, profile_use, profile_generate, c_compiler):
207     if not pgo:
208         return
210     # Enabling PGO through MOZ_PGO only and not --enable* flags.
211     if not profile_use and not profile_generate:
212         return
214     if profile_use and profile_generate:
215         die("Cannot build with --enable-profile-use and --enable-profile-generate.")
217     want_cross = (len(profile_use) and profile_use[0] == "cross") or (
218         len(profile_generate) and profile_generate[0] == "cross"
219     )
221     if not want_cross:
222         return
224     if c_compiler.type == "gcc":
225         die("Cannot use cross-language PGO with GCC.")
227     return True
230 set_config("MOZ_PGO_RUST", moz_pgo_rust)
232 # LTO
233 # ==============================================================
235 option(
236     "--enable-lto",
237     env="MOZ_LTO",
238     nargs="*",
239     choices=("full", "thin", "cross"),
240     help="Enable LTO",
243 option(
244     env="MOZ_LD64_KNOWN_GOOD",
245     nargs=1,
246     help="Indicate that ld64 is free of symbol aliasing bugs.",
249 imply_option("MOZ_LD64_KNOWN_GOOD", depends_if("MOZ_AUTOMATION")(lambda _: True))
252 @depends(
253     "--enable-lto",
254     c_compiler,
255     select_linker,
256     "MOZ_LD64_KNOWN_GOOD",
257     target,
258     "--enable-profile-generate",
259     pass_manager.enabled,
260     "--enable-profile-use",
261     "MOZ_AUTOMATION",
263 @imports("multiprocessing")
264 def lto(
265     values,
266     c_compiler,
267     select_linker,
268     ld64_known_good,
269     target,
270     instrumented_build,
271     pass_manager,
272     pgo_build,
273     moz_automation,
275     cflags = []
276     ldflags = []
277     enabled = None
278     rust_lto = False
280     if not values:
281         return
283     # Sanitize LTO modes.
284     if "full" in values and "thin" in values:
285         die("incompatible --enable-lto choices 'full' and 'thin'")
287     # If a value was given to --enable-lto, use that.  Otherwise, make the lto
288     # mode explicit, using full with gcc, and full or thin with clang depending
289     # on the performance benefit.
290     # Defaulting to full LTO is costly in terms of compilation time, so we only
291     # default to it if MOZ_AUTOMATION and PGO are on, and for some platforms.
292     # Based on speedometer3 scores, full lto + pgo is beneficial for Linux and
293     # Windows for x86_64 targets.
294     if values == () or values == ("cross",):
295         if c_compiler.type == "gcc":
296             values += ("full",)
297         elif (
298             pgo_build
299             and moz_automation
300             and target.os in ("WINNT", "GNU")
301             and target.cpu == "x86_64"
302         ):
303             values += ("full",)
304         else:
305             values += ("thin",)
307     if instrumented_build:
308         log.warning("Disabling LTO because --enable-profile-generate is specified")
309         return
311     if c_compiler.type == "gcc":
312         if "cross" in values:
313             die("Cross-language LTO is not supported with GCC.")
314         if "thin" in values:
315             die(
316                 "gcc does not support thin LTO. Use `--enable-lto` "
317                 "to enable full LTO for gcc."
318             )
320     if (
321         target.kernel == "Darwin"
322         and "cross" in values
323         and select_linker.KIND == "ld64"
324         and not ld64_known_good
325     ):
326         die(
327             "The Mac linker is known to have a bug that affects cross-language "
328             "LTO.  If you know that your linker is free from this bug, please "
329             "set the environment variable `MOZ_LD64_KNOWN_GOOD=1` and re-run "
330             "configure."
331         )
333     if c_compiler.type == "clang":
334         if "full" in values:
335             cflags.append("-flto")
336             ldflags.append("-flto")
337         else:
338             cflags.append("-flto=thin")
339             ldflags.append("-flto=thin")
341         if target.os == "Android" and "cross" in values:
342             # Work around https://github.com/rust-lang/rust/issues/90088
343             # by enabling the highest level of SSE the rust targets default
344             # to.
345             # https://github.com/rust-lang/rust/blob/bdfcb88e8b6203ccb46a2fb6649979b773efc8ac/compiler/rustc_target/src/spec/i686_linux_android.rs#L13
346             # https://github.com/rust-lang/rust/blob/8d1083e319841624f64400e1524805a40d725439/compiler/rustc_target/src/spec/x86_64_linux_android.rs#L7
347             if target.cpu == "x86":
348                 ldflags.append("-Wl,-plugin-opt=-mattr=+ssse3")
349             elif target.cpu == "x86_64":
350                 ldflags.append("-Wl,-plugin-opt=-mattr=+sse4.2")
351     elif c_compiler.type == "clang-cl":
352         if "full" in values:
353             cflags.append("-flto")
354         else:
355             cflags.append("-flto=thin")
356         # With clang-cl, -flto can only be used with -c or -fuse-ld=lld.
357         # AC_TRY_LINKs during configure don't have -c, so pass -fuse-ld=lld.
358         cflags.append("-fuse-ld=lld")
360         # Explicitly set the CPU to optimize for so the linker doesn't
361         # choose a poor default.  Rust compilation by default uses the
362         # pentium4 CPU on x86:
363         #
364         # https://github.com/rust-lang/rust/blob/049a49b91151a88c95fa0d62a53fd0a0ac2c3af9/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs#L5
365         #
366         # which specifically supports "long" (multi-byte) nops.  See
367         # https://bugzilla.mozilla.org/show_bug.cgi?id=1568450#c8 for details.
368         #
369         # The pentium4 seems like kind of a weird CPU to optimize for, but
370         # it seems to have worked out OK thus far.  LLVM does not seem to
371         # specifically schedule code for the pentium4's deep pipeline, so
372         # that probably contributes to it being an OK default for our
373         # purposes.
374         if target.cpu == "x86":
375             ldflags.append("-mllvm:-mcpu=pentium4")
376         # This is also the CPU that Rust uses.  The LLVM source code
377         # recommends this as the "generic 64-bit specific x86 processor model":
378         #
379         # https://github.com/llvm/llvm-project/blob/e7694f34ab6a12b8bb480cbfcb396d0a64fe965f/llvm/lib/Target/X86/X86.td#L1165-L1187
380         if target.cpu == "x86_64":
381             ldflags.append("-mllvm:-mcpu=x86-64")
382         # We do not need special flags for arm64.  Hooray for fixed-length
383         # instruction sets.
384     else:
385         num_cores = multiprocessing.cpu_count()
386         cflags.append("-flto")
387         cflags.append("-flifetime-dse=1")
389         ldflags.append("-flto=%s" % num_cores)
390         ldflags.append("-flifetime-dse=1")
392     # Tell LTO not to inline functions above a certain size, to mitigate
393     # binary size growth while still getting good performance.
394     # (For hot functions, PGO will put a multiplier on this limit.)
395     if target.os == "WINNT":
396         ldflags.append("-mllvm:-import-instr-limit=10")
397     elif target.kernel == "Darwin":
398         ldflags.append("-Wl,-mllvm,-import-instr-limit=10")
399     elif c_compiler.type == "clang":
400         ldflags.append("-Wl,-plugin-opt=-import-instr-limit=10")
402     # If we're using the new pass manager, we can also enable the new PM
403     # during LTO. Further we can use the resulting size savings to increase
404     # the import limit in hot functions.
405     if pass_manager:
406         if target.os == "WINNT":
407             if c_compiler.version >= "12.0.0" and c_compiler.version < "13.0.0":
408                 ldflags.append("-opt:ltonewpassmanager")
409             if c_compiler.version >= "12.0.0":
410                 ldflags.append("-mllvm:-import-hot-multiplier=30")
411         elif target.kernel == "Darwin":
412             ldflags.append("-Wl,-mllvm,-import-hot-multiplier=30")
413         else:
414             if c_compiler.version < "13.0.0":
415                 ldflags.append("-Wl,-plugin-opt=new-pass-manager")
416             ldflags.append("-Wl,-plugin-opt=-import-hot-multiplier=30")
418     # Pick Rust LTO mode in case of cross lTO. Thin is the default.
419     if "cross" in values:
420         rust_lto = "full" if "full" in values else "thin"
421     else:
422         rust_lto = ""
424     return namespace(
425         enabled=True,
426         cflags=cflags,
427         ldflags=ldflags,
428         rust_lto=rust_lto,
429     )
432 add_old_configure_assignment("MOZ_LTO", lto.enabled)
433 set_config("MOZ_LTO", lto.enabled)
434 set_define("MOZ_LTO", lto.enabled)
435 set_config("MOZ_LTO_CFLAGS", lto.cflags)
436 set_config("MOZ_LTO_LDFLAGS", lto.ldflags)
437 set_config("MOZ_LTO_RUST_CROSS", lto.rust_lto)
438 add_old_configure_assignment("MOZ_LTO_CFLAGS", lto.cflags)
439 add_old_configure_assignment("MOZ_LTO_LDFLAGS", lto.ldflags)