@@ -44,7 +44,7 @@ rust_lto_flag = rule(
44
44
)
45
45
46
46
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.
48
48
49
49
Args:
50
50
ctx (ctx): The calling rule's context object.
@@ -76,13 +76,39 @@ def _determine_lto_object_format(ctx, toolchain, crate_info):
76
76
# generating object files entirely.
77
77
return "only_bitcode"
78
78
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.
82
81
return "only_object"
83
82
else :
84
83
return "object_and_bitcode"
85
84
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
+
86
112
def construct_lto_arguments (ctx , toolchain , crate_info ):
87
113
"""Returns a list of 'rustc' flags to configure link time optimization.
88
114
@@ -101,10 +127,16 @@ def construct_lto_arguments(ctx, toolchain, crate_info):
101
127
return []
102
128
103
129
format = _determine_lto_object_format (ctx , toolchain , crate_info )
130
+ xlang_enabled = _determine_experimental_xlang_lto (ctx , toolchain , crate_info )
104
131
args = []
105
132
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 ):
108
140
args .append ("lto={}" .format (mode ))
109
141
110
142
if format == "object_and_bitcode" :
@@ -117,4 +149,7 @@ def construct_lto_arguments(ctx, toolchain, crate_info):
117
149
else :
118
150
fail ("unrecognized LTO object format {}" .format (format ))
119
151
152
+ if xlang_enabled :
153
+ args .append ("linker-plugin-lto" )
154
+
120
155
return ["-C{}" .format (arg ) for arg in args ]
0 commit comments