Rollup merge of #58057 - michaelwoerister:stabilize-xlto, r=alexcrichton
Stabilize linker-plugin based LTO (aka cross-language LTO) This PR stabilizes [linker plugin based LTO](https://github.com/rust-lang/rust/issues/49879), also known as "cross-language LTO" because it allows for doing inlining and other optimizations across language boundaries in mixed Rust/C/C++ projects. As described in the tracking issue, it works by making `rustc` emit LLVM bitcode instead of machine code, the same as `clang` does. A linker with the proper plugin (like LLD) can then run (Thin)LTO across all modules. The feature has been implemented over a number of pull requests and there are various [codegen](https://github.com/rust-lang/rust/blob/master/src/test/codegen/no-dllimport-w-cross-lang-lto.rs) and [run](https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/cross-lang-lto-clang)-[make](https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/cross-lang-lto-upstream-rlibs) [tests](https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/cross-lang-lto) that make sure that it keeps working. It also works for building big projects like [Firefox](https://treeherder.mozilla.org/#/jobs?repo=try&revision=2ce2d5ddcea6fbff790503eac406954e469b2f5d). The PR makes the feature available under the `-C linker-plugin-lto` flag. As discussed in the tracking issue it is not cross-language specific and also not LLD specific. `-C linker-plugin-lto` is descriptive of what it does. If someone has a better name, let me know `:)`
This commit is contained in:
@@ -13,4 +13,5 @@
|
|||||||
- [Targets](targets/index.md)
|
- [Targets](targets/index.md)
|
||||||
- [Built-in Targets](targets/built-in.md)
|
- [Built-in Targets](targets/built-in.md)
|
||||||
- [Custom Targets](targets/custom.md)
|
- [Custom Targets](targets/custom.md)
|
||||||
|
- [Linker-plugin based LTO](linker-plugin-lto.md)
|
||||||
- [Contributing to `rustc`](contributing.md)
|
- [Contributing to `rustc`](contributing.md)
|
||||||
108
src/doc/rustc/src/linker-plugin-lto.md
Normal file
108
src/doc/rustc/src/linker-plugin-lto.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# Linker-plugin-LTO
|
||||||
|
|
||||||
|
The `-C linker-plugin-lto` flag allows for deferring the LTO optimization
|
||||||
|
to the actual linking step, which in turn allows for performing
|
||||||
|
interprocedural optimizations across programming language boundaries if
|
||||||
|
all the object files being linked were created by LLVM based toolchains.
|
||||||
|
The prime example here would be linking Rust code together with
|
||||||
|
Clang-compiled C/C++ code.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
There are two main cases how linker plugin based LTO can be used:
|
||||||
|
|
||||||
|
- compiling a Rust `staticlib` that is used as a C ABI dependency
|
||||||
|
- compiling a Rust binary where `rustc` invokes the linker
|
||||||
|
|
||||||
|
In both cases the Rust code has to be compiled with `-C linker-plugin-lto` and
|
||||||
|
the C/C++ code with `-flto` or `-flto=thin` so that object files are emitted
|
||||||
|
as LLVM bitcode.
|
||||||
|
|
||||||
|
### Rust `staticlib` as dependency in C/C++ program
|
||||||
|
|
||||||
|
In this case the Rust compiler just has to make sure that the object files in
|
||||||
|
the `staticlib` are in the right format. For linking, a linker with the
|
||||||
|
LLVM plugin must be used (e.g. LLD).
|
||||||
|
|
||||||
|
Using `rustc` directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Compile the Rust staticlib
|
||||||
|
rustc --crate-type=staticlib -Clinker-plugin-lto -Copt-level=2 ./lib.rs
|
||||||
|
# Compile the C code with `-flto=thin`
|
||||||
|
clang -c -O2 -flto=thin -o main.o ./main.c
|
||||||
|
# Link everything, making sure that we use an appropriate linker
|
||||||
|
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `cargo`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Compile the Rust staticlib
|
||||||
|
RUSTFLAGS="-Clinker-plugin-lto" cargo build --release
|
||||||
|
# Compile the C code with `-flto=thin`
|
||||||
|
clang -c -O2 -flto=thin -o main.o ./main.c
|
||||||
|
# Link everything, making sure that we use an appropriate linker
|
||||||
|
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
|
||||||
|
```
|
||||||
|
|
||||||
|
### C/C++ code as a dependency in Rust
|
||||||
|
|
||||||
|
In this case the linker will be invoked by `rustc`. We again have to make sure
|
||||||
|
that an appropriate linker is used.
|
||||||
|
|
||||||
|
Using `rustc` directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Compile C code with `-flto`
|
||||||
|
clang ./clib.c -flto=thin -c -o ./clib.o -O2
|
||||||
|
# Create a static library from the C code
|
||||||
|
ar crus ./libxyz.a ./clib.o
|
||||||
|
|
||||||
|
# Invoke `rustc` with the additional arguments
|
||||||
|
rustc -Clinker-plugin-lto -L. -Copt-level=2 -Clinker=clang -Clink-arg=-fuse-ld=lld ./main.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `cargo` directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Compile C code with `-flto`
|
||||||
|
clang ./clib.c -flto=thin -c -o ./clib.o -O2
|
||||||
|
# Create a static library from the C code
|
||||||
|
ar crus ./libxyz.a ./clib.o
|
||||||
|
|
||||||
|
# Set the linking arguments via RUSTFLAGS
|
||||||
|
RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explicitly specifying the linker plugin to be used by `rustc`
|
||||||
|
|
||||||
|
If one wants to use a linker other than LLD, the LLVM linker plugin has to be
|
||||||
|
specified explicitly. Otherwise the linker cannot read the object files. The
|
||||||
|
path to the plugin is passed as an argument to the `-Clinker-plugin-lto`
|
||||||
|
option:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rustc -Clinker-plugin-lto="/path/to/LLVMgold.so" -L. -Copt-level=2 ./main.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Toolchain Compatibility
|
||||||
|
|
||||||
|
In order for this kind of LTO to work, the LLVM linker plugin must be able to
|
||||||
|
handle the LLVM bitcode produced by both `rustc` and `clang`.
|
||||||
|
|
||||||
|
Best results are achieved by using a `rustc` and `clang` that are based on the
|
||||||
|
exact same version of LLVM. One can use `rustc -vV` in order to view the LLVM
|
||||||
|
used by a given `rustc` version. Note that the version number given
|
||||||
|
here is only an approximation as Rust sometimes uses unstable revisions of
|
||||||
|
LLVM. However, the approximation is usually reliable.
|
||||||
|
|
||||||
|
The following table shows known good combinations of toolchain versions.
|
||||||
|
|
||||||
|
| | Clang 7 | Clang 8 |
|
||||||
|
|-----------|-----------|-----------|
|
||||||
|
| Rust 1.34 | ✗ | ✓ |
|
||||||
|
| Rust 1.35 | ✗ | ✓(?) |
|
||||||
|
|
||||||
|
Note that the compatibility policy for this feature might change in the future.
|
||||||
@@ -96,18 +96,18 @@ pub enum LtoCli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
pub enum CrossLangLto {
|
pub enum LinkerPluginLto {
|
||||||
LinkerPlugin(PathBuf),
|
LinkerPlugin(PathBuf),
|
||||||
LinkerPluginAuto,
|
LinkerPluginAuto,
|
||||||
Disabled
|
Disabled
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CrossLangLto {
|
impl LinkerPluginLto {
|
||||||
pub fn enabled(&self) -> bool {
|
pub fn enabled(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
CrossLangLto::LinkerPlugin(_) |
|
LinkerPluginLto::LinkerPlugin(_) |
|
||||||
CrossLangLto::LinkerPluginAuto => true,
|
LinkerPluginLto::LinkerPluginAuto => true,
|
||||||
CrossLangLto::Disabled => false,
|
LinkerPluginLto::Disabled => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -812,7 +812,7 @@ macro_rules! options {
|
|||||||
pub const parse_lto: Option<&str> =
|
pub const parse_lto: Option<&str> =
|
||||||
Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \
|
Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \
|
||||||
`fat`, or omitted");
|
`fat`, or omitted");
|
||||||
pub const parse_cross_lang_lto: Option<&str> =
|
pub const parse_linker_plugin_lto: Option<&str> =
|
||||||
Some("either a boolean (`yes`, `no`, `on`, `off`, etc), \
|
Some("either a boolean (`yes`, `no`, `on`, `off`, etc), \
|
||||||
or the path to the linker plugin");
|
or the path to the linker plugin");
|
||||||
pub const parse_merge_functions: Option<&str> =
|
pub const parse_merge_functions: Option<&str> =
|
||||||
@@ -821,7 +821,7 @@ macro_rules! options {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod $mod_set {
|
mod $mod_set {
|
||||||
use super::{$struct_name, Passes, Sanitizer, LtoCli, CrossLangLto};
|
use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto};
|
||||||
use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel};
|
use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -1037,22 +1037,22 @@ macro_rules! options {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cross_lang_lto(slot: &mut CrossLangLto, v: Option<&str>) -> bool {
|
fn parse_linker_plugin_lto(slot: &mut LinkerPluginLto, v: Option<&str>) -> bool {
|
||||||
if v.is_some() {
|
if v.is_some() {
|
||||||
let mut bool_arg = None;
|
let mut bool_arg = None;
|
||||||
if parse_opt_bool(&mut bool_arg, v) {
|
if parse_opt_bool(&mut bool_arg, v) {
|
||||||
*slot = if bool_arg.unwrap() {
|
*slot = if bool_arg.unwrap() {
|
||||||
CrossLangLto::LinkerPluginAuto
|
LinkerPluginLto::LinkerPluginAuto
|
||||||
} else {
|
} else {
|
||||||
CrossLangLto::Disabled
|
LinkerPluginLto::Disabled
|
||||||
};
|
};
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*slot = match v {
|
*slot = match v {
|
||||||
None => CrossLangLto::LinkerPluginAuto,
|
None => LinkerPluginLto::LinkerPluginAuto,
|
||||||
Some(path) => CrossLangLto::LinkerPlugin(PathBuf::from(path)),
|
Some(path) => LinkerPluginLto::LinkerPlugin(PathBuf::from(path)),
|
||||||
};
|
};
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -1145,6 +1145,10 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
|
|||||||
"allow the linker to link its default libraries"),
|
"allow the linker to link its default libraries"),
|
||||||
linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED],
|
linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED],
|
||||||
"Linker flavor"),
|
"Linker flavor"),
|
||||||
|
linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled,
|
||||||
|
parse_linker_plugin_lto, [TRACKED],
|
||||||
|
"generate build artifacts that are compatible with linker-based LTO."),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||||
@@ -1383,8 +1387,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
|||||||
"make the current crate share its generic instantiations"),
|
"make the current crate share its generic instantiations"),
|
||||||
chalk: bool = (false, parse_bool, [TRACKED],
|
chalk: bool = (false, parse_bool, [TRACKED],
|
||||||
"enable the experimental Chalk-based trait solving engine"),
|
"enable the experimental Chalk-based trait solving engine"),
|
||||||
cross_lang_lto: CrossLangLto = (CrossLangLto::Disabled, parse_cross_lang_lto, [TRACKED],
|
|
||||||
"generate build artifacts that are compatible with linker-based LTO."),
|
|
||||||
no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED],
|
no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"),
|
"don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"),
|
||||||
no_leak_check: bool = (false, parse_bool, [UNTRACKED],
|
no_leak_check: bool = (false, parse_bool, [UNTRACKED],
|
||||||
@@ -2440,7 +2442,7 @@ mod dep_tracking {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use super::{CrateType, DebugInfo, ErrorOutputType, OptLevel, OutputTypes,
|
use super::{CrateType, DebugInfo, ErrorOutputType, OptLevel, OutputTypes,
|
||||||
Passes, Sanitizer, LtoCli, CrossLangLto};
|
Passes, Sanitizer, LtoCli, LinkerPluginLto};
|
||||||
use syntax::feature_gate::UnstableFeatures;
|
use syntax::feature_gate::UnstableFeatures;
|
||||||
use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel, TargetTriple};
|
use rustc_target::spec::{MergeFunctions, PanicStrategy, RelroLevel, TargetTriple};
|
||||||
use syntax::edition::Edition;
|
use syntax::edition::Edition;
|
||||||
@@ -2507,7 +2509,7 @@ mod dep_tracking {
|
|||||||
impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
|
impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
|
||||||
impl_dep_tracking_hash_via_hash!(TargetTriple);
|
impl_dep_tracking_hash_via_hash!(TargetTriple);
|
||||||
impl_dep_tracking_hash_via_hash!(Edition);
|
impl_dep_tracking_hash_via_hash!(Edition);
|
||||||
impl_dep_tracking_hash_via_hash!(CrossLangLto);
|
impl_dep_tracking_hash_via_hash!(LinkerPluginLto);
|
||||||
|
|
||||||
impl_dep_tracking_hash_for_sortable_vec_of!(String);
|
impl_dep_tracking_hash_for_sortable_vec_of!(String);
|
||||||
impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf);
|
impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf);
|
||||||
@@ -2572,7 +2574,7 @@ mod tests {
|
|||||||
use crate::lint;
|
use crate::lint;
|
||||||
use crate::middle::cstore;
|
use crate::middle::cstore;
|
||||||
use crate::session::config::{build_configuration, build_session_options_and_crate_config};
|
use crate::session::config::{build_configuration, build_session_options_and_crate_config};
|
||||||
use crate::session::config::{LtoCli, CrossLangLto};
|
use crate::session::config::{LtoCli, LinkerPluginLto};
|
||||||
use crate::session::build_session;
|
use crate::session::build_session;
|
||||||
use crate::session::search_paths::SearchPath;
|
use crate::session::search_paths::SearchPath;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
@@ -3105,6 +3107,10 @@ mod tests {
|
|||||||
opts = reference.clone();
|
opts = reference.clone();
|
||||||
opts.cg.panic = Some(PanicStrategy::Abort);
|
opts.cg.panic = Some(PanicStrategy::Abort);
|
||||||
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
||||||
|
|
||||||
|
opts = reference.clone();
|
||||||
|
opts.cg.linker_plugin_lto = LinkerPluginLto::LinkerPluginAuto;
|
||||||
|
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -3231,10 +3237,6 @@ mod tests {
|
|||||||
opts.debugging_opts.relro_level = Some(RelroLevel::Full);
|
opts.debugging_opts.relro_level = Some(RelroLevel::Full);
|
||||||
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
||||||
|
|
||||||
opts = reference.clone();
|
|
||||||
opts.debugging_opts.cross_lang_lto = CrossLangLto::LinkerPluginAuto;
|
|
||||||
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
|
||||||
|
|
||||||
opts = reference.clone();
|
opts = reference.clone();
|
||||||
opts.debugging_opts.merge_functions = Some(MergeFunctions::Disabled);
|
opts.debugging_opts.merge_functions = Some(MergeFunctions::Disabled);
|
||||||
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
||||||
|
|||||||
@@ -1267,7 +1267,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
|||||||
// bitcode during ThinLTO. Therefore we disallow dynamic linking on MSVC
|
// bitcode during ThinLTO. Therefore we disallow dynamic linking on MSVC
|
||||||
// when compiling for LLD ThinLTO. This way we can validly just not generate
|
// when compiling for LLD ThinLTO. This way we can validly just not generate
|
||||||
// the `dllimport` attributes and `__imp_` symbols in that case.
|
// the `dllimport` attributes and `__imp_` symbols in that case.
|
||||||
if sess.opts.debugging_opts.cross_lang_lto.enabled() &&
|
if sess.opts.cg.linker_plugin_lto.enabled() &&
|
||||||
sess.opts.cg.prefer_dynamic &&
|
sess.opts.cg.prefer_dynamic &&
|
||||||
sess.target.target.options.is_like_msvc {
|
sess.target.target.options.is_like_msvc {
|
||||||
sess.err("Linker plugin based LTO is not supported together with \
|
sess.err("Linker plugin based LTO is not supported together with \
|
||||||
|
|||||||
@@ -857,7 +857,7 @@ fn link_args(cmd: &mut dyn Linker,
|
|||||||
codegen_results: &CodegenResults) {
|
codegen_results: &CodegenResults) {
|
||||||
|
|
||||||
// Linker plugins should be specified early in the list of arguments
|
// Linker plugins should be specified early in the list of arguments
|
||||||
cmd.cross_lang_lto();
|
cmd.linker_plugin_lto();
|
||||||
|
|
||||||
// The default library location, we need this to find the runtime.
|
// The default library location, we need this to find the runtime.
|
||||||
// The location of crates will be determined as needed.
|
// The location of crates will be determined as needed.
|
||||||
@@ -1491,7 +1491,7 @@ fn are_upstream_rust_objects_already_included(sess: &Session) -> bool {
|
|||||||
Lto::Thin => {
|
Lto::Thin => {
|
||||||
// If we defer LTO to the linker, we haven't run LTO ourselves, so
|
// If we defer LTO to the linker, we haven't run LTO ourselves, so
|
||||||
// any upstream object files have not been copied yet.
|
// any upstream object files have not been copied yet.
|
||||||
!sess.opts.debugging_opts.cross_lang_lto.enabled()
|
!sess.opts.cg.linker_plugin_lto.enabled()
|
||||||
}
|
}
|
||||||
Lto::No |
|
Lto::No |
|
||||||
Lto::ThinLocal => false,
|
Lto::ThinLocal => false,
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ pub(crate) fn run_thin(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|||||||
let symbol_white_list = symbol_white_list.iter()
|
let symbol_white_list = symbol_white_list.iter()
|
||||||
.map(|c| c.as_ptr())
|
.map(|c| c.as_ptr())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if cgcx.opts.debugging_opts.cross_lang_lto.enabled() {
|
if cgcx.opts.cg.linker_plugin_lto.enabled() {
|
||||||
unreachable!("We should never reach this case if the LTO step \
|
unreachable!("We should never reach this case if the LTO step \
|
||||||
is deferred to the linker");
|
is deferred to the linker");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|||||||
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
|
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
|
||||||
.unwrap_or(llvm::CodeGenOptLevel::None);
|
.unwrap_or(llvm::CodeGenOptLevel::None);
|
||||||
let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
|
let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
|
||||||
(cgcx.lto != Lto::Fat && cgcx.opts.debugging_opts.cross_lang_lto.enabled());
|
(cgcx.lto != Lto::Fat && cgcx.opts.cg.linker_plugin_lto.enabled());
|
||||||
with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| {
|
with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| {
|
||||||
llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm);
|
llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm);
|
||||||
llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm);
|
llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm);
|
||||||
|
|||||||
@@ -275,12 +275,12 @@ impl CodegenCx<'ll, 'tcx> {
|
|||||||
self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) &&
|
self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) &&
|
||||||
// ThinLTO can't handle this workaround in all cases, so we don't
|
// ThinLTO can't handle this workaround in all cases, so we don't
|
||||||
// emit the attrs. Instead we make them unnecessary by disallowing
|
// emit the attrs. Instead we make them unnecessary by disallowing
|
||||||
// dynamic linking when cross-language LTO is enabled.
|
// dynamic linking when linker plugin based LTO is enabled.
|
||||||
!self.tcx.sess.opts.debugging_opts.cross_lang_lto.enabled();
|
!self.tcx.sess.opts.cg.linker_plugin_lto.enabled();
|
||||||
|
|
||||||
// If this assertion triggers, there's something wrong with commandline
|
// If this assertion triggers, there's something wrong with commandline
|
||||||
// argument validation.
|
// argument validation.
|
||||||
debug_assert!(!(self.tcx.sess.opts.debugging_opts.cross_lang_lto.enabled() &&
|
debug_assert!(!(self.tcx.sess.opts.cg.linker_plugin_lto.enabled() &&
|
||||||
self.tcx.sess.target.target.options.is_like_msvc &&
|
self.tcx.sess.target.target.options.is_like_msvc &&
|
||||||
self.tcx.sess.opts.cg.prefer_dynamic));
|
self.tcx.sess.opts.cg.prefer_dynamic));
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
|
|||||||
use rustc::middle::dependency_format::Linkage;
|
use rustc::middle::dependency_format::Linkage;
|
||||||
use rustc::session::Session;
|
use rustc::session::Session;
|
||||||
use rustc::session::config::{self, CrateType, OptLevel, DebugInfo,
|
use rustc::session::config::{self, CrateType, OptLevel, DebugInfo,
|
||||||
CrossLangLto, Lto};
|
LinkerPluginLto, Lto};
|
||||||
use rustc::ty::TyCtxt;
|
use rustc::ty::TyCtxt;
|
||||||
use rustc_target::spec::{LinkerFlavor, LldFlavor};
|
use rustc_target::spec::{LinkerFlavor, LldFlavor};
|
||||||
use serialize::{json, Encoder};
|
use serialize::{json, Encoder};
|
||||||
@@ -127,7 +127,7 @@ pub trait Linker {
|
|||||||
fn subsystem(&mut self, subsystem: &str);
|
fn subsystem(&mut self, subsystem: &str);
|
||||||
fn group_start(&mut self);
|
fn group_start(&mut self);
|
||||||
fn group_end(&mut self);
|
fn group_end(&mut self);
|
||||||
fn cross_lang_lto(&mut self);
|
fn linker_plugin_lto(&mut self);
|
||||||
// Should have been finalize(self), but we don't support self-by-value on trait objects (yet?).
|
// Should have been finalize(self), but we don't support self-by-value on trait objects (yet?).
|
||||||
fn finalize(&mut self) -> Command;
|
fn finalize(&mut self) -> Command;
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ impl<'a> GccLinker<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_cross_lang_lto_args(&mut self, plugin_path: Option<&OsStr>) {
|
fn push_linker_plugin_lto_args(&mut self, plugin_path: Option<&OsStr>) {
|
||||||
if let Some(plugin_path) = plugin_path {
|
if let Some(plugin_path) = plugin_path {
|
||||||
let mut arg = OsString::from("-plugin=");
|
let mut arg = OsString::from("-plugin=");
|
||||||
arg.push(plugin_path);
|
arg.push(plugin_path);
|
||||||
@@ -454,16 +454,16 @@ impl<'a> Linker for GccLinker<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cross_lang_lto(&mut self) {
|
fn linker_plugin_lto(&mut self) {
|
||||||
match self.sess.opts.debugging_opts.cross_lang_lto {
|
match self.sess.opts.cg.linker_plugin_lto {
|
||||||
CrossLangLto::Disabled => {
|
LinkerPluginLto::Disabled => {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
CrossLangLto::LinkerPluginAuto => {
|
LinkerPluginLto::LinkerPluginAuto => {
|
||||||
self.push_cross_lang_lto_args(None);
|
self.push_linker_plugin_lto_args(None);
|
||||||
}
|
}
|
||||||
CrossLangLto::LinkerPlugin(ref path) => {
|
LinkerPluginLto::LinkerPlugin(ref path) => {
|
||||||
self.push_cross_lang_lto_args(Some(path.as_os_str()));
|
self.push_linker_plugin_lto_args(Some(path.as_os_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -697,7 +697,7 @@ impl<'a> Linker for MsvcLinker<'a> {
|
|||||||
fn group_start(&mut self) {}
|
fn group_start(&mut self) {}
|
||||||
fn group_end(&mut self) {}
|
fn group_end(&mut self) {}
|
||||||
|
|
||||||
fn cross_lang_lto(&mut self) {
|
fn linker_plugin_lto(&mut self) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -865,7 +865,7 @@ impl<'a> Linker for EmLinker<'a> {
|
|||||||
fn group_start(&mut self) {}
|
fn group_start(&mut self) {}
|
||||||
fn group_end(&mut self) {}
|
fn group_end(&mut self) {}
|
||||||
|
|
||||||
fn cross_lang_lto(&mut self) {
|
fn linker_plugin_lto(&mut self) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1047,7 +1047,7 @@ impl<'a> Linker for WasmLd<'a> {
|
|||||||
fn group_start(&mut self) {}
|
fn group_start(&mut self) {}
|
||||||
fn group_end(&mut self) {}
|
fn group_end(&mut self) {}
|
||||||
|
|
||||||
fn cross_lang_lto(&mut self) {
|
fn linker_plugin_lto(&mut self) {
|
||||||
// Do nothing for now
|
// Do nothing for now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1207,6 +1207,6 @@ impl<'a> Linker for PtxLinker<'a> {
|
|||||||
fn group_end(&mut self) {
|
fn group_end(&mut self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cross_lang_lto(&mut self) {
|
fn linker_plugin_lto(&mut self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ impl ModuleConfig {
|
|||||||
self.time_passes = sess.time_passes();
|
self.time_passes = sess.time_passes();
|
||||||
self.inline_threshold = sess.opts.cg.inline_threshold;
|
self.inline_threshold = sess.opts.cg.inline_threshold;
|
||||||
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode ||
|
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode ||
|
||||||
sess.opts.debugging_opts.cross_lang_lto.enabled();
|
sess.opts.cg.linker_plugin_lto.enabled();
|
||||||
let embed_bitcode = sess.target.target.options.embed_bitcode ||
|
let embed_bitcode = sess.target.target.options.embed_bitcode ||
|
||||||
sess.opts.debugging_opts.embed_bitcode;
|
sess.opts.debugging_opts.embed_bitcode;
|
||||||
if embed_bitcode {
|
if embed_bitcode {
|
||||||
@@ -737,7 +737,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
|
|||||||
// If the linker does LTO, we don't have to do it. Note that we
|
// If the linker does LTO, we don't have to do it. Note that we
|
||||||
// keep doing full LTO, if it is requested, as not to break the
|
// keep doing full LTO, if it is requested, as not to break the
|
||||||
// assumption that the output will be a single module.
|
// assumption that the output will be a single module.
|
||||||
let linker_does_lto = cgcx.opts.debugging_opts.cross_lang_lto.enabled();
|
let linker_does_lto = cgcx.opts.cg.linker_plugin_lto.enabled();
|
||||||
|
|
||||||
// When we're automatically doing ThinLTO for multi-codegen-unit
|
// When we're automatically doing ThinLTO for multi-codegen-unit
|
||||||
// builds we don't actually want to LTO the allocator modules if
|
// builds we don't actually want to LTO the allocator modules if
|
||||||
@@ -1883,7 +1883,7 @@ pub fn pre_lto_bitcode_filename(module_name: &str) -> String {
|
|||||||
fn msvc_imps_needed(tcx: TyCtxt) -> bool {
|
fn msvc_imps_needed(tcx: TyCtxt) -> bool {
|
||||||
// This should never be true (because it's not supported). If it is true,
|
// This should never be true (because it's not supported). If it is true,
|
||||||
// something is wrong with commandline arg validation.
|
// something is wrong with commandline arg validation.
|
||||||
assert!(!(tcx.sess.opts.debugging_opts.cross_lang_lto.enabled() &&
|
assert!(!(tcx.sess.opts.cg.linker_plugin_lto.enabled() &&
|
||||||
tcx.sess.target.target.options.is_like_msvc &&
|
tcx.sess.target.target.options.is_like_msvc &&
|
||||||
tcx.sess.opts.cg.prefer_dynamic));
|
tcx.sess.opts.cg.prefer_dynamic));
|
||||||
|
|
||||||
@@ -1891,6 +1891,6 @@ fn msvc_imps_needed(tcx: TyCtxt) -> bool {
|
|||||||
tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateType::Rlib) &&
|
tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateType::Rlib) &&
|
||||||
// ThinLTO can't handle this workaround in all cases, so we don't
|
// ThinLTO can't handle this workaround in all cases, so we don't
|
||||||
// emit the `__imp_` symbols. Instead we make them unnecessary by disallowing
|
// emit the `__imp_` symbols. Instead we make them unnecessary by disallowing
|
||||||
// dynamic linking when cross-language LTO is enabled.
|
// dynamic linking when linker plugin LTO is enabled.
|
||||||
!tcx.sess.opts.debugging_opts.cross_lang_lto.enabled()
|
!tcx.sess.opts.cg.linker_plugin_lto.enabled()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
// no-prefer-dynamic
|
// no-prefer-dynamic
|
||||||
// only-msvc
|
// only-msvc
|
||||||
// compile-flags: -Z cross-lang-lto
|
// compile-flags: -C linker-plugin-lto
|
||||||
|
|
||||||
#![crate_type = "rlib"]
|
#![crate_type = "rlib"]
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
// no-prefer-dynamic
|
// no-prefer-dynamic
|
||||||
// ignore-tidy-linelength
|
// ignore-tidy-linelength
|
||||||
// compile-flags: -C no-prepopulate-passes -C panic=abort -Z cross-lang-lto -Cpasses=name-anon-globals
|
// compile-flags: -C no-prepopulate-passes -C panic=abort -C linker-plugin-lto -Cpasses=name-anon-globals
|
||||||
|
|
||||||
#![crate_type = "staticlib"]
|
#![crate_type = "staticlib"]
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
all: cpp-executable rust-executable
|
all: cpp-executable rust-executable
|
||||||
|
|
||||||
cpp-executable:
|
cpp-executable:
|
||||||
$(RUSTC) -Zcross-lang-lto=on -o $(TMPDIR)/librustlib-xlto.a -Copt-level=2 -Ccodegen-units=1 ./rustlib.rs
|
$(RUSTC) -Clinker-plugin-lto=on -o $(TMPDIR)/librustlib-xlto.a -Copt-level=2 -Ccodegen-units=1 ./rustlib.rs
|
||||||
$(CLANG) -flto=thin -fuse-ld=lld -L $(TMPDIR) -lrustlib-xlto -o $(TMPDIR)/cmain ./cmain.c -O3
|
$(CLANG) -flto=thin -fuse-ld=lld -L $(TMPDIR) -lrustlib-xlto -o $(TMPDIR)/cmain ./cmain.c -O3
|
||||||
# Make sure we don't find a call instruction to the function we expect to
|
# Make sure we don't find a call instruction to the function we expect to
|
||||||
# always be inlined.
|
# always be inlined.
|
||||||
@@ -20,6 +20,6 @@ cpp-executable:
|
|||||||
rust-executable:
|
rust-executable:
|
||||||
$(CLANG) ./clib.c -flto=thin -c -o $(TMPDIR)/clib.o -O2
|
$(CLANG) ./clib.c -flto=thin -c -o $(TMPDIR)/clib.o -O2
|
||||||
(cd $(TMPDIR); $(AR) crus ./libxyz.a ./clib.o)
|
(cd $(TMPDIR); $(AR) crus ./libxyz.a ./clib.o)
|
||||||
$(RUSTC) -Zcross-lang-lto=on -L$(TMPDIR) -Copt-level=2 -Clinker=$(CLANG) -Clink-arg=-fuse-ld=lld ./main.rs -o $(TMPDIR)/rsmain
|
$(RUSTC) -Clinker-plugin-lto=on -L$(TMPDIR) -Copt-level=2 -Clinker=$(CLANG) -Clink-arg=-fuse-ld=lld ./main.rs -o $(TMPDIR)/rsmain
|
||||||
llvm-objdump -d $(TMPDIR)/rsmain | $(CGREP) -e "call.*c_never_inlined"
|
llvm-objdump -d $(TMPDIR)/rsmain | $(CGREP) -e "call.*c_never_inlined"
|
||||||
llvm-objdump -d $(TMPDIR)/rsmain | $(CGREP) -v -e "call.*c_always_inlined"
|
llvm-objdump -d $(TMPDIR)/rsmain | $(CGREP) -v -e "call.*c_always_inlined"
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
ifndef IS_WINDOWS
|
ifndef IS_WINDOWS
|
||||||
|
|
||||||
# This test makes sure that we don't loose upstream object files when compiling
|
# This test makes sure that we don't loose upstream object files when compiling
|
||||||
# staticlibs with -Zcross-lang-lto
|
# staticlibs with -C linker-plugin-lto
|
||||||
|
|
||||||
all: staticlib.rs upstream.rs
|
all: staticlib.rs upstream.rs
|
||||||
$(RUSTC) upstream.rs -Z cross-lang-lto -Ccodegen-units=1
|
$(RUSTC) upstream.rs -C linker-plugin-lto -Ccodegen-units=1
|
||||||
|
|
||||||
# Check No LTO
|
# Check No LTO
|
||||||
$(RUSTC) staticlib.rs -Z cross-lang-lto -Ccodegen-units=1 -L. -o $(TMPDIR)/staticlib.a
|
$(RUSTC) staticlib.rs -C linker-plugin-lto -Ccodegen-units=1 -L. -o $(TMPDIR)/staticlib.a
|
||||||
(cd $(TMPDIR); $(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-ar x ./staticlib.a)
|
(cd $(TMPDIR); $(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-ar x ./staticlib.a)
|
||||||
# Make sure the upstream object file was included
|
# Make sure the upstream object file was included
|
||||||
ls $(TMPDIR)/upstream.*.rcgu.o
|
ls $(TMPDIR)/upstream.*.rcgu.o
|
||||||
@@ -20,8 +20,8 @@ all: staticlib.rs upstream.rs
|
|||||||
rm $(TMPDIR)/*
|
rm $(TMPDIR)/*
|
||||||
|
|
||||||
# Check ThinLTO
|
# Check ThinLTO
|
||||||
$(RUSTC) upstream.rs -Z cross-lang-lto -Ccodegen-units=1 -Clto=thin
|
$(RUSTC) upstream.rs -C linker-plugin-lto -Ccodegen-units=1 -Clto=thin
|
||||||
$(RUSTC) staticlib.rs -Z cross-lang-lto -Ccodegen-units=1 -Clto=thin -L. -o $(TMPDIR)/staticlib.a
|
$(RUSTC) staticlib.rs -C linker-plugin-lto -Ccodegen-units=1 -Clto=thin -L. -o $(TMPDIR)/staticlib.a
|
||||||
(cd $(TMPDIR); $(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-ar x ./staticlib.a)
|
(cd $(TMPDIR); $(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-ar x ./staticlib.a)
|
||||||
ls $(TMPDIR)/upstream.*.rcgu.o
|
ls $(TMPDIR)/upstream.*.rcgu.o
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ ifndef IS_WINDOWS
|
|||||||
|
|
||||||
# This test makes sure that the object files we generate are actually
|
# This test makes sure that the object files we generate are actually
|
||||||
# LLVM bitcode files (as used by linker LTO plugins) when compiling with
|
# LLVM bitcode files (as used by linker LTO plugins) when compiling with
|
||||||
# -Z cross-lang-lto.
|
# -Clinker-plugin-lto.
|
||||||
|
|
||||||
# this only succeeds for bitcode files
|
# this only succeeds for bitcode files
|
||||||
ASSERT_IS_BITCODE_OBJ=($(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-bcanalyzer $(1))
|
ASSERT_IS_BITCODE_OBJ=($(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-bcanalyzer $(1))
|
||||||
EXTRACT_OBJS=(cd $(TMPDIR); rm -f ./*.o; $(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-ar x $(1))
|
EXTRACT_OBJS=(cd $(TMPDIR); rm -f ./*.o; $(LD_LIB_PATH_ENVVAR)=$(REAL_LD_LIBRARY_PATH) llvm-ar x $(1))
|
||||||
|
|
||||||
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto=on -Ccodegen-units=1
|
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Clinker-plugin-lto -Ccodegen-units=1
|
||||||
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto=on -Ccodegen-units=1 --emit=obj
|
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Clinker-plugin-lto -Ccodegen-units=1 --emit=obj
|
||||||
|
|
||||||
all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib
|
all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user