Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / build / moz.configure / lto-pgo.configure
blob99355877447e48e8c7f8569794e99375ad5b254d
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
13 option(
14     "--enable-profile-generate",
15     env="MOZ_PROFILE_GENERATE",
16     nargs="?",
17     choices=("cross",),
18     help="Build a PGO instrumented binary",
21 imply_option("MOZ_PGO", depends_if("--enable-profile-generate")(lambda _: True))
23 set_config(
24     "MOZ_PROFILE_GENERATE", depends_if("--enable-profile-generate")(lambda _: True)
27 set_define(
28     "MOZ_PROFILE_GENERATE", depends_if("--enable-profile-generate")(lambda _: True)
31 add_old_configure_assignment(
32     "MOZ_PROFILE_GENERATE", 1, when="--enable-profile-generate"
35 option(
36     "--enable-profile-use",
37     env="MOZ_PROFILE_USE",
38     nargs="?",
39     choices=("cross",),
40     help="Use a generated profile during the build",
43 option(
44     "--with-pgo-profile-path",
45     help="Path to the directory with unmerged profile data to use during the build",
46     nargs=1,
49 imply_option("MOZ_PGO", depends_if("--enable-profile-use")(lambda _: True))
51 set_config("MOZ_PROFILE_USE", depends_if("--enable-profile-use")(lambda _: True))
54 @depends(
55     "--with-pgo-profile-path",
56     "--enable-profile-use",
57     llvm_profdata,
58     build_environment,
60 @imports("os")
61 def pgo_profile_path(path, pgo_use, profdata, build_env):
62     topobjdir = build_env.topobjdir
63     if topobjdir.endswith("/js/src"):
64         topobjdir = topobjdir[:-7]
66     if not path:
67         return os.path.join(topobjdir, "instrumented", "merged.profdata")
68     if path and not pgo_use:
69         die("Pass --enable-profile-use to use --with-pgo-profile-path.")
70     if path and not profdata:
71         die("LLVM_PROFDATA must be set to process the pgo profile.")
72     if not os.path.isfile(path[0]):
73         die("Argument to --with-pgo-profile-path must be a file.")
74     if not os.path.isabs(path[0]):
75         die("Argument to --with-pgo-profile-path must be an absolute path.")
76     return path[0]
79 set_config("PGO_PROFILE_PATH", pgo_profile_path)
82 @depends(c_compiler, pgo_profile_path, target_is_windows)
83 @imports("multiprocessing")
84 def pgo_flags(compiler, profdata, target_is_windows):
85     if compiler.type == "gcc":
86         return namespace(
87             gen_cflags=["-fprofile-generate"],
88             gen_ldflags=["-fprofile-generate"],
89             use_cflags=["-fprofile-use", "-fprofile-correction", "-Wcoverage-mismatch"],
90             use_ldflags=["-fprofile-use"],
91         )
93     if compiler.type in ("clang-cl", "clang"):
94         prefix = ""
95         if compiler.type == "clang-cl":
96             prefix = "/clang:"
97             gen_ldflags = None
98         else:
99             gen_ldflags = ["-fprofile-generate"]
101         gen_cflags = [prefix + "-fprofile-generate"]
102         if target_is_windows:
103             # native llvm-profdata.exe on Windows can't read profile data
104             # if name compression is enabled (which cross-compiling enables
105             # by default)
106             gen_cflags += ["-mllvm", "-enable-name-compression=false"]
108         return namespace(
109             gen_cflags=gen_cflags,
110             gen_ldflags=gen_ldflags,
111             use_cflags=[
112                 prefix + "-fprofile-use=%s" % profdata,
113                 # Some error messages about mismatched profile data
114                 # come in via -Wbackend-plugin, so disable those too.
115                 "-Wno-error=backend-plugin",
116             ],
117             use_ldflags=[],
118         )
121 set_config("PROFILE_GEN_CFLAGS", pgo_flags.gen_cflags)
122 set_config("PROFILE_GEN_LDFLAGS", pgo_flags.gen_ldflags)
123 set_config("PROFILE_USE_CFLAGS", pgo_flags.use_cflags)
124 set_config("PROFILE_USE_LDFLAGS", pgo_flags.use_ldflags)
126 option(
127     "--with-pgo-jarlog",
128     help="Use the provided jarlog file when packaging during a profile-use " "build",
129     nargs=1,
132 set_config("PGO_JARLOG_PATH", depends_if("--with-pgo-jarlog")(lambda p: p))
135 @depends("MOZ_PGO", "--enable-profile-use", "--enable-profile-generate", c_compiler)
136 def moz_pgo_rust(pgo, profile_use, profile_generate, c_compiler):
137     if not pgo:
138         return
140     # Enabling PGO through MOZ_PGO only and not --enable* flags.
141     if not profile_use and not profile_generate:
142         return
144     if profile_use and profile_generate:
145         die("Cannot build with --enable-profile-use and --enable-profile-generate.")
147     want_cross = (len(profile_use) and profile_use[0] == "cross") or (
148         len(profile_generate) and profile_generate[0] == "cross"
149     )
151     if not want_cross:
152         return
154     if c_compiler.type == "gcc":
155         die("Cannot use cross-language PGO with GCC.")
157     return True
160 set_config("MOZ_PGO_RUST", moz_pgo_rust)
162 # LTO
163 # ==============================================================
165 option(
166     "--enable-lto",
167     env="MOZ_LTO",
168     nargs="*",
169     choices=("full", "thin", "cross"),
170     help="Enable LTO",
173 option(
174     env="MOZ_LD64_KNOWN_GOOD",
175     nargs=1,
176     help="Indicate that ld64 is free of symbol aliasing bugs.",
179 imply_option("MOZ_LD64_KNOWN_GOOD", depends_if("MOZ_AUTOMATION")(lambda _: True))
182 @depends(
183     "--enable-lto",
184     c_compiler,
185     select_linker,
186     "MOZ_LD64_KNOWN_GOOD",
187     target,
188     "--enable-profile-generate",
189     pass_manager.enabled,
190     "--enable-profile-use",
191     "MOZ_AUTOMATION",
193 @imports("multiprocessing")
194 def lto(
195     values,
196     c_compiler,
197     select_linker,
198     ld64_known_good,
199     target,
200     instrumented_build,
201     pass_manager,
202     pgo_build,
203     moz_automation,
205     cflags = []
206     ldflags = []
207     enabled = None
208     rust_lto = False
210     if not values:
211         return
213     # Sanitize LTO modes.
214     if "full" in values and "thin" in values:
215         die("incompatible --enable-lto choices 'full' and 'thin'")
217     # If a value was given to --enable-lto, use that.  Otherwise, make the lto
218     # mode explicit, using full with gcc, and full or thin with clang depending
219     # on the performance benefit.
220     # Defaulting to full LTO is costly in terms of compilation time, so we only
221     # default to it if MOZ_AUTOMATION and PGO are on, and for some platforms.
222     # Based on speedometer3 scores, full lto + pgo is beneficial for Linux and
223     # Windows for x86_64 targets.
224     if values == () or values == ("cross",):
225         if c_compiler.type == "gcc":
226             values += ("full",)
227         elif (
228             pgo_build
229             and moz_automation
230             and target.os in ("WINNT", "GNU")
231             and target.cpu == "x86_64"
232         ):
233             values += ("full",)
234         else:
235             values += ("thin",)
237     if instrumented_build:
238         log.warning("Disabling LTO because --enable-profile-generate is specified")
239         return
241     if c_compiler.type == "gcc":
242         if "cross" in values:
243             die("Cross-language LTO is not supported with GCC.")
244         if "thin" in values:
245             die(
246                 "gcc does not support thin LTO. Use `--enable-lto` "
247                 "to enable full LTO for gcc."
248             )
250     if (
251         target.kernel == "Darwin"
252         and target.os == "OSX"
253         and "cross" in values
254         and select_linker.KIND == "ld64"
255         and not ld64_known_good
256     ):
257         die(
258             "The Mac linker is known to have a bug that affects cross-language "
259             "LTO.  If you know that your linker is free from this bug, please "
260             "set the environment variable `MOZ_LD64_KNOWN_GOOD=1` and re-run "
261             "configure."
262         )
264     if c_compiler.type == "clang":
265         if "full" in values:
266             cflags.append("-flto")
267             ldflags.append("-flto")
268         else:
269             cflags.append("-flto=thin")
270             ldflags.append("-flto=thin")
272         if target.os == "Android" and "cross" in values:
273             # Work around https://github.com/rust-lang/rust/issues/90088
274             # by enabling the highest level of SSE the rust targets default
275             # to.
276             # https://github.com/rust-lang/rust/blob/bdfcb88e8b6203ccb46a2fb6649979b773efc8ac/compiler/rustc_target/src/spec/i686_linux_android.rs#L13
277             # https://github.com/rust-lang/rust/blob/8d1083e319841624f64400e1524805a40d725439/compiler/rustc_target/src/spec/x86_64_linux_android.rs#L7
278             if target.cpu == "x86":
279                 ldflags.append("-Wl,-plugin-opt=-mattr=+ssse3")
280             elif target.cpu == "x86_64":
281                 ldflags.append("-Wl,-plugin-opt=-mattr=+sse4.2")
282     elif c_compiler.type == "clang-cl":
283         if "full" in values:
284             cflags.append("-flto")
285         else:
286             cflags.append("-flto=thin")
287         # With clang-cl, -flto can only be used with -c or -fuse-ld=lld.
288         # AC_TRY_LINKs during configure don't have -c, so pass -fuse-ld=lld.
289         cflags.append("-fuse-ld=lld")
291         # Explicitly set the CPU to optimize for so the linker doesn't
292         # choose a poor default.  Rust compilation by default uses the
293         # pentium4 CPU on x86:
294         #
295         # https://github.com/rust-lang/rust/blob/049a49b91151a88c95fa0d62a53fd0a0ac2c3af9/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs#L5
296         #
297         # which specifically supports "long" (multi-byte) nops.  See
298         # https://bugzilla.mozilla.org/show_bug.cgi?id=1568450#c8 for details.
299         #
300         # The pentium4 seems like kind of a weird CPU to optimize for, but
301         # it seems to have worked out OK thus far.  LLVM does not seem to
302         # specifically schedule code for the pentium4's deep pipeline, so
303         # that probably contributes to it being an OK default for our
304         # purposes.
305         if target.cpu == "x86":
306             ldflags.append("-mllvm:-mcpu=pentium4")
307         # This is also the CPU that Rust uses.  The LLVM source code
308         # recommends this as the "generic 64-bit specific x86 processor model":
309         #
310         # https://github.com/llvm/llvm-project/blob/e7694f34ab6a12b8bb480cbfcb396d0a64fe965f/llvm/lib/Target/X86/X86.td#L1165-L1187
311         if target.cpu == "x86_64":
312             ldflags.append("-mllvm:-mcpu=x86-64")
313         # We do not need special flags for arm64.  Hooray for fixed-length
314         # instruction sets.
315     else:
316         num_cores = multiprocessing.cpu_count()
317         cflags.append("-flto")
318         cflags.append("-flifetime-dse=1")
320         ldflags.append("-flto=%s" % num_cores)
321         ldflags.append("-flifetime-dse=1")
323     # Tell LTO not to inline functions above a certain size, to mitigate
324     # binary size growth while still getting good performance.
325     # (For hot functions, PGO will put a multiplier on this limit.)
326     if target.os == "WINNT":
327         ldflags.append("-mllvm:-import-instr-limit=10")
328     elif target.os == "OSX":
329         ldflags.append("-Wl,-mllvm,-import-instr-limit=10")
330     elif c_compiler.type == "clang":
331         ldflags.append("-Wl,-plugin-opt=-import-instr-limit=10")
333     # If we're using the new pass manager, we can also enable the new PM
334     # during LTO. Further we can use the resulting size savings to increase
335     # the import limit in hot functions.
336     if pass_manager:
337         if target.os == "WINNT":
338             if c_compiler.version >= "12.0.0" and c_compiler.version < "13.0.0":
339                 ldflags.append("-opt:ltonewpassmanager")
340             if c_compiler.version >= "12.0.0":
341                 ldflags.append("-mllvm:-import-hot-multiplier=30")
342         elif target.os == "OSX":
343             ldflags.append("-Wl,-mllvm,-import-hot-multiplier=30")
344         else:
345             if c_compiler.version < "13.0.0":
346                 ldflags.append("-Wl,-plugin-opt=new-pass-manager")
347             ldflags.append("-Wl,-plugin-opt=-import-hot-multiplier=30")
349     # Pick Rust LTO mode in case of cross lTO. Thin is the default.
350     if "cross" in values:
351         rust_lto = "full" if "full" in values else "thin"
352     else:
353         rust_lto = ""
355     return namespace(
356         enabled=True,
357         cflags=cflags,
358         ldflags=ldflags,
359         rust_lto=rust_lto,
360     )
363 add_old_configure_assignment("MOZ_LTO", lto.enabled)
364 set_config("MOZ_LTO", lto.enabled)
365 set_define("MOZ_LTO", lto.enabled)
366 set_config("MOZ_LTO_CFLAGS", lto.cflags)
367 set_config("MOZ_LTO_LDFLAGS", lto.ldflags)
368 set_config("MOZ_LTO_RUST_CROSS", lto.rust_lto)
369 add_old_configure_assignment("MOZ_LTO_CFLAGS", lto.cflags)
370 add_old_configure_assignment("MOZ_LTO_LDFLAGS", lto.ldflags)