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:
Mazdak Farrokhzad
2019-02-13 04:36:59 +01:00
committed by GitHub
15 changed files with 172 additions and 61 deletions

View File

@@ -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)

View 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.

View File

@@ -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());

View File

@@ -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 \

View File

@@ -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,

View File

@@ -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");
} }

View File

@@ -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);

View File

@@ -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));

View File

@@ -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) {
} }
} }

View File

@@ -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()
} }

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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"

View File

@@ -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

View File

@@ -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