blob: 49ac42b6ea43a0b46d6953dbab9234febd3ccddf [file]
"""A module defining Rust link time optimization (lto) rules"""
load("//rust/private:utils.bzl", "is_exec_configuration")
_LTO_MODES = [
# Do nothing, let the user manually handle LTO.
"manual",
# Default. No mode has been explicitly set, rustc will do "thin local" LTO
# between the codegen units of a single crate.
"unspecified",
# LTO has been explicitly turned "off".
"off",
# Perform "thin" LTO. This is similar to "fat" but takes significantly less
# time to run, but provides similar performance improvements.
#
# See: <http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html>
"thin",
# Perform "fat"/full LTO.
"fat",
]
RustLtoInfo = provider(
doc = "A provider describing the link time optimization setting.",
fields = {"mode": "string: The LTO mode specified via a build setting."},
)
def _rust_lto_flag_impl(ctx):
value = ctx.build_setting_value
if value not in _LTO_MODES:
msg = "{NAME} build setting allowed to take values [{VALUES}], but was set to: {ACTUAL}".format(
NAME = ctx.label,
VALUES = ", ".join(["'{}'".format(m) for m in _LTO_MODES]),
ACTUAL = value,
)
fail(msg)
return RustLtoInfo(mode = value)
rust_lto_flag = rule(
doc = "A build setting which specifies the link time optimization mode used when building Rust code. Allowed values are: ".format(_LTO_MODES),
implementation = _rust_lto_flag_impl,
build_setting = config.string(flag = True),
)
def _determine_lto_object_format(ctx, toolchain, crate_info):
"""Determines if we should run LTO and what bitcode should get included in a built artifact.
Args:
ctx (ctx): The calling rule's context object.
toolchain (rust_toolchain): The current target's `rust_toolchain`.
crate_info (CrateInfo): The CrateInfo provider of the target crate.
Returns:
string: Returns one of only_object, only_bitcode, object_and_bitcode.
"""
# Even if LTO is enabled don't use it for actions being built in the exec
# configuration, e.g. build scripts and proc-macros. This mimics Cargo.
if is_exec_configuration(ctx):
return "only_object"
mode = toolchain.lto.mode
if mode in ["off", "unspecified"]:
return "only_object"
perform_linking = crate_info.type in ["bin", "staticlib", "cdylib"]
is_dynamic = crate_info.type in ["dylib", "cdylib", "proc-macro"]
needs_object = perform_linking or is_dynamic
# At this point we know LTO is enabled, otherwise we would have returned above.
if not needs_object:
# If we're building an 'rlib' and LTO is enabled, then we can skip
# generating object files entirely.
return "only_bitcode"
elif crate_info.type in ["dylib", "proc-macro"]:
# If we're a dylib and we're running LTO, then only emit object code
# because 'rustc' doesn't currently support LTO with dylibs.
# proc-macros do not benefit from LTO, and cannot be dynamically linked with LTO.
return "only_object"
else:
return "object_and_bitcode"
def construct_lto_arguments(ctx, toolchain, crate_info):
"""Returns a list of 'rustc' flags to configure link time optimization.
Args:
ctx (ctx): The calling rule's context object.
toolchain (rust_toolchain): The current target's `rust_toolchain`.
crate_info (CrateInfo): The CrateInfo provider of the target crate.
Returns:
list: A list of strings that are valid flags for 'rustc'.
"""
mode = toolchain.lto.mode
# The user is handling LTO on their own, don't add any arguments.
if mode == "manual":
return []
format = _determine_lto_object_format(ctx, toolchain, crate_info)
args = []
# proc-macros do not benefit from LTO, and cannot be dynamically linked with LTO.
if mode in ["thin", "fat", "off"] and not is_exec_configuration(ctx) and crate_info.type != "proc-macro":
args.append("lto={}".format(mode))
if format == "object_and_bitcode":
# Embedding LLVM bitcode in object files is `rustc's` default.
args.extend([])
elif format == "only_object":
args.extend(["embed-bitcode=no"])
elif format == "only_bitcode":
args.extend(["linker-plugin-lto"])
else:
fail("unrecognized LTO object format {}".format(format))
return ["-C{}".format(arg) for arg in args]