Skip to content

Commit cfdef01

Browse files
committed
start, add experimental_cross_language_lto feature
1 parent bb74a65 commit cfdef01

File tree

4 files changed

+59
-6
lines changed

4 files changed

+59
-6
lines changed

rust/private/lto.bzl

+41-6
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ rust_lto_flag = rule(
4444
)
4545

4646
def _determine_lto_object_format(ctx, toolchain, crate_info):
47-
"""Determines if we should run LTO and what bitcode should get included in a built artifact.
47+
"""Determines what bitcode should get included in a built artifact.
4848
4949
Args:
5050
ctx (ctx): The calling rule's context object.
@@ -76,13 +76,39 @@ def _determine_lto_object_format(ctx, toolchain, crate_info):
7676
# generating object files entirely.
7777
return "only_bitcode"
7878
elif crate_info.type in ["dylib", "proc-macro"]:
79-
# If we're a dylib and we're running LTO, then only emit object code
80-
# because 'rustc' doesn't currently support LTO with dylibs.
81-
# proc-macros do not benefit from LTO, and cannot be dynamically linked with LTO.
79+
# If we're a dylib or a proc-macro and we're running LTO, then only emit
80+
# object code because 'rustc' doesn't currently support LTO for these targets.
8281
return "only_object"
8382
else:
8483
return "object_and_bitcode"
8584

85+
def _determine_experimental_xlang_lto(ctx, toolchain, crate_info):
86+
"""Determines if we should use Linker-plugin-based LTO, to enable cross language optimizations.
87+
88+
'rustc' has a `linker-plugin-lto` codegen option which delays LTO to the actual linking step.
89+
If your C/C++ code is built with an LLVM toolchain (e.g. clang) and was built with LTO enabled,
90+
then the linker can perform optimizations across programming language boundaries.
91+
92+
See <https://doc.rust-lang.org/rustc/linker-plugin-lto.html>
93+
94+
Args:
95+
ctx (ctx): The calling rule's context object.
96+
toolchain (rust_toolchain): The current target's `rust_toolchain`.
97+
crate_info (CrateInfo): The CrateInfo provider of the target crate.
98+
99+
Returns:
100+
bool: Whether or not to specify `-Clinker-plugin-lto` when building this crate.
101+
"""
102+
103+
feature_enabled = toolchain._experimental_cross_language_lto
104+
rust_lto_enabled = toolchain.lto.mode in ["thin", "fat"]
105+
correct_crate_type = crate_info.type in ["bin"]
106+
107+
# TODO(parkmycar): We could try to detect if LTO is enabled for C code using
108+
# `ctx.fragments.cpp.copts` but I'm not sure how reliable that is.
109+
110+
return feature_enabled and rust_lto_enabled and correct_crate_type and not is_exec_configuration(ctx)
111+
86112
def construct_lto_arguments(ctx, toolchain, crate_info):
87113
"""Returns a list of 'rustc' flags to configure link time optimization.
88114
@@ -101,10 +127,16 @@ def construct_lto_arguments(ctx, toolchain, crate_info):
101127
return []
102128

103129
format = _determine_lto_object_format(ctx, toolchain, crate_info)
130+
xlang_enabled = _determine_experimental_xlang_lto(ctx, toolchain, crate_info)
104131
args = []
105132

106-
# proc-macros do not benefit from LTO, and cannot be dynamically linked with LTO.
107-
if mode in ["thin", "fat", "off"] and not is_exec_configuration(ctx) and crate_info.type != "proc-macro":
133+
# Only tell `rustc` to use LTO if it's enabled, the crate we're currently building has bitcode
134+
# embeded, and we're not building in the exec configuration.
135+
#
136+
# We skip running LTO when building for the exec configuration because the exec config is used
137+
# for local tools, like build scripts or proc-macros, and LTO isn't really needed in those
138+
# scenarios. Note, this also mimics Cargo's behavior.
139+
if mode in ["thin", "fat", "off"] and format != "only_object" and not is_exec_configuration(ctx):
108140
args.append("lto={}".format(mode))
109141

110142
if format == "object_and_bitcode":
@@ -117,4 +149,7 @@ def construct_lto_arguments(ctx, toolchain, crate_info):
117149
else:
118150
fail("unrecognized LTO object format {}".format(format))
119151

152+
if xlang_enabled:
153+
args.append("linker-plugin-lto")
154+
120155
return ["-C{}".format(arg) for arg in args]

rust/settings/BUILD.bazel

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ load(
77
"clippy_toml",
88
"codegen_units",
99
"error_format",
10+
"experimental_cross_language_lto",
1011
"experimental_link_std_dylib",
1112
"experimental_per_crate_rustc_flag",
1213
"experimental_use_cc_common_link",
@@ -60,6 +61,8 @@ codegen_units()
6061

6162
error_format()
6263

64+
experimental_cross_language_lto()
65+
6366
experimental_link_std_dylib()
6467

6568
experimental_per_crate_rustc_flag()

rust/settings/settings.bzl

+11
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ def lto():
5656
build_setting_default = "unspecified",
5757
)
5858

59+
def experimental_cross_language_lto():
60+
"""A build setting which specifies whether or not to specify `linker-plugin-lto` and perform \
61+
cross language optimizations.
62+
63+
See: <https://doc.rust-lang.org/rustc/linker-plugin-lto.html>
64+
"""
65+
bool_flag(
66+
name = "experimental_cross_language_lto",
67+
build_setting_default = False,
68+
)
69+
5970
def rename_first_party_crates():
6071
"""A flag controlling whether to rename first-party crates such that their names \
6172
encode the Bazel package and target name, instead of just the target name.

rust/toolchain.bzl

+4
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ def _rust_toolchain_impl(ctx):
698698
_rename_first_party_crates = rename_first_party_crates,
699699
_third_party_dir = third_party_dir,
700700
_pipelined_compilation = pipelined_compilation,
701+
_experimental_cross_language_lto = ctx.attr._experimental_cross_language_lto[BuildSettingInfo].value,
701702
_experimental_link_std_dylib = _experimental_link_std_dylib(ctx),
702703
_experimental_use_cc_common_link = _experimental_use_cc_common_link(ctx),
703704
_experimental_use_global_allocator = experimental_use_global_allocator,
@@ -885,6 +886,9 @@ rust_toolchain = rule(
885886
"_codegen_units": attr.label(
886887
default = Label("//rust/settings:codegen_units"),
887888
),
889+
"_experimental_cross_language_lto": attr.label(
890+
default = Label("//rust/settings:experimental_cross_language_lto"),
891+
),
888892
"_experimental_use_coverage_metadata_files": attr.label(
889893
default = Label("//rust/settings:experimental_use_coverage_metadata_files"),
890894
),

0 commit comments

Comments
 (0)