Rollup merge of #50569 - michaelwoerister:cross-lang-lto-2, r=alexcrichton
Allow for specifying a linker plugin for cross-language LTO This PR makes the `-Zcross-lang-lto` flag optionally take the path to the `LLVMgold.so` linker plugin. If this path is specified, `rustc` will invoke the linker with the correct arguments (i.e. `-plugin` and various `-plugin-opt`s). This can be used to ergonomically enable cross-language LTO for Rust programs with C/C++ dependencies: ``` clang -O2 test.c -otest.o -c -flto=thin llvm-ar -rv libxxx.a test.o rustc -L. main.rs -Zcross-lang-lto=/usr/lib64/LLVMgold.so -O -Clink-arg=-fuse-ld=gold ``` - Note that in theory this should work with Gold, LLD, and newer versions of binutils' LD but on my current system I could only get it to work with Gold. - Also note that this will work best if the Clang version and Rust's LLVM version are close enough. Clang 6.0 works well with the current nightly. r? @alexcrichton
This commit is contained in:
commit
dd40277d0f
6 changed files with 118 additions and 13 deletions
|
@ -95,6 +95,23 @@ pub enum Lto {
|
||||||
Fat,
|
Fat,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
|
pub enum CrossLangLto {
|
||||||
|
LinkerPlugin(PathBuf),
|
||||||
|
NoLink,
|
||||||
|
Disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrossLangLto {
|
||||||
|
pub fn embed_bitcode(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
CrossLangLto::LinkerPlugin(_) |
|
||||||
|
CrossLangLto::NoLink => true,
|
||||||
|
CrossLangLto::Disabled => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Hash)]
|
||||||
pub enum DebugInfoLevel {
|
pub enum DebugInfoLevel {
|
||||||
NoDebugInfo,
|
NoDebugInfo,
|
||||||
|
@ -412,6 +429,7 @@ top_level_options!(
|
||||||
|
|
||||||
// Remap source path prefixes in all output (messages, object files, debug, etc)
|
// Remap source path prefixes in all output (messages, object files, debug, etc)
|
||||||
remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED],
|
remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED],
|
||||||
|
|
||||||
edition: Edition [TRACKED],
|
edition: Edition [TRACKED],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -777,11 +795,15 @@ macro_rules! options {
|
||||||
Some("`string` or `string=string`");
|
Some("`string` or `string=string`");
|
||||||
pub const parse_lto: Option<&'static str> =
|
pub const parse_lto: Option<&'static str> =
|
||||||
Some("one of `thin`, `fat`, or omitted");
|
Some("one of `thin`, `fat`, or omitted");
|
||||||
|
pub const parse_cross_lang_lto: Option<&'static str> =
|
||||||
|
Some("either a boolean (`yes`, `no`, `on`, `off`, etc), `no-link`, \
|
||||||
|
or the path to the linker plugin");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod $mod_set {
|
mod $mod_set {
|
||||||
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto};
|
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto,
|
||||||
|
CrossLangLto};
|
||||||
use rustc_target::spec::{LinkerFlavor, PanicStrategy, RelroLevel};
|
use rustc_target::spec::{LinkerFlavor, PanicStrategy, RelroLevel};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -986,6 +1008,26 @@ macro_rules! options {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_cross_lang_lto(slot: &mut CrossLangLto, v: Option<&str>) -> bool {
|
||||||
|
if v.is_some() {
|
||||||
|
let mut bool_arg = None;
|
||||||
|
if parse_opt_bool(&mut bool_arg, v) {
|
||||||
|
*slot = if bool_arg.unwrap() {
|
||||||
|
CrossLangLto::NoLink
|
||||||
|
} else {
|
||||||
|
CrossLangLto::Disabled
|
||||||
|
};
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*slot = match v {
|
||||||
|
None |
|
||||||
|
Some("no-link") => CrossLangLto::NoLink,
|
||||||
|
Some(path) => CrossLangLto::LinkerPlugin(PathBuf::from(path)),
|
||||||
|
};
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) }
|
) }
|
||||||
|
|
||||||
|
@ -1295,7 +1337,7 @@ 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: bool = (false, parse_bool, [TRACKED],
|
cross_lang_lto: CrossLangLto = (CrossLangLto::Disabled, parse_cross_lang_lto, [TRACKED],
|
||||||
"generate build artifacts that are compatible with linker-based LTO."),
|
"generate build artifacts that are compatible with linker-based LTO."),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2327,7 +2369,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, DebugInfoLevel, ErrorOutputType, Lto, OptLevel, OutputTypes,
|
use super::{CrateType, DebugInfoLevel, ErrorOutputType, Lto, OptLevel, OutputTypes,
|
||||||
Passes, Sanitizer};
|
Passes, Sanitizer, CrossLangLto};
|
||||||
use syntax::feature_gate::UnstableFeatures;
|
use syntax::feature_gate::UnstableFeatures;
|
||||||
use rustc_target::spec::{PanicStrategy, RelroLevel, TargetTriple};
|
use rustc_target::spec::{PanicStrategy, RelroLevel, TargetTriple};
|
||||||
use syntax::edition::Edition;
|
use syntax::edition::Edition;
|
||||||
|
@ -2391,6 +2433,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_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);
|
||||||
|
@ -2455,7 +2498,7 @@ mod tests {
|
||||||
use lint;
|
use lint;
|
||||||
use middle::cstore;
|
use middle::cstore;
|
||||||
use session::config::{build_configuration, build_session_options_and_crate_config};
|
use session::config::{build_configuration, build_session_options_and_crate_config};
|
||||||
use session::config::Lto;
|
use session::config::{Lto, CrossLangLto};
|
||||||
use session::build_session;
|
use session::build_session;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
@ -3111,6 +3154,10 @@ mod tests {
|
||||||
opts = reference.clone();
|
opts = reference.clone();
|
||||||
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::NoLink;
|
||||||
|
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -657,6 +657,13 @@ impl Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn target_cpu(&self) -> &str {
|
||||||
|
match self.opts.cg.target_cpu {
|
||||||
|
Some(ref s) => &**s,
|
||||||
|
None => &*self.target.target.options.cpu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn must_not_eliminate_frame_pointers(&self) -> bool {
|
pub fn must_not_eliminate_frame_pointers(&self) -> bool {
|
||||||
if let Some(x) = self.opts.cg.force_frame_pointers {
|
if let Some(x) = self.opts.cg.force_frame_pointers {
|
||||||
x
|
x
|
||||||
|
|
|
@ -970,6 +970,9 @@ fn link_args(cmd: &mut Linker,
|
||||||
out_filename: &Path,
|
out_filename: &Path,
|
||||||
trans: &CrateTranslation) {
|
trans: &CrateTranslation) {
|
||||||
|
|
||||||
|
// Linker plugins should be specified early in the list of arguments
|
||||||
|
cmd.cross_lang_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.
|
||||||
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
|
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
|
||||||
|
|
|
@ -21,7 +21,8 @@ use back::symbol_export;
|
||||||
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
|
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, DebugInfoLevel};
|
use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel,
|
||||||
|
CrossLangLto};
|
||||||
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,6 +128,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);
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
@ -434,6 +436,42 @@ impl<'a> Linker for GccLinker<'a> {
|
||||||
self.linker_arg("--end-group");
|
self.linker_arg("--end-group");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cross_lang_lto(&mut self) {
|
||||||
|
match self.sess.opts.debugging_opts.cross_lang_lto {
|
||||||
|
CrossLangLto::Disabled |
|
||||||
|
CrossLangLto::NoLink => {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
CrossLangLto::LinkerPlugin(ref path) => {
|
||||||
|
self.linker_arg(&format!("-plugin={}", path.display()));
|
||||||
|
|
||||||
|
let opt_level = match self.sess.opts.optimize {
|
||||||
|
config::OptLevel::No => "O0",
|
||||||
|
config::OptLevel::Less => "O1",
|
||||||
|
config::OptLevel::Default => "O2",
|
||||||
|
config::OptLevel::Aggressive => "O3",
|
||||||
|
config::OptLevel::Size => "Os",
|
||||||
|
config::OptLevel::SizeMin => "Oz",
|
||||||
|
};
|
||||||
|
|
||||||
|
self.linker_arg(&format!("-plugin-opt={}", opt_level));
|
||||||
|
self.linker_arg(&format!("-plugin-opt=mcpu={}", self.sess.target_cpu()));
|
||||||
|
|
||||||
|
match self.sess.opts.cg.lto {
|
||||||
|
config::Lto::Thin |
|
||||||
|
config::Lto::ThinLocal => {
|
||||||
|
self.linker_arg(&format!("-plugin-opt=thin"));
|
||||||
|
}
|
||||||
|
config::Lto::Fat |
|
||||||
|
config::Lto::Yes |
|
||||||
|
config::Lto::No => {
|
||||||
|
// default to regular LTO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MsvcLinker<'a> {
|
pub struct MsvcLinker<'a> {
|
||||||
|
@ -666,6 +704,10 @@ impl<'a> Linker for MsvcLinker<'a> {
|
||||||
// MSVC doesn't need group indicators
|
// MSVC doesn't need group indicators
|
||||||
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) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EmLinker<'a> {
|
pub struct EmLinker<'a> {
|
||||||
|
@ -832,6 +874,10 @@ impl<'a> Linker for EmLinker<'a> {
|
||||||
// Appears not necessary on Emscripten
|
// Appears not necessary on Emscripten
|
||||||
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) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec<String> {
|
fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec<String> {
|
||||||
|
@ -984,4 +1030,8 @@ impl Linker for WasmLd {
|
||||||
// Not needed for now with LLD
|
// Not needed for now with LLD
|
||||||
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) {
|
||||||
|
// Do nothing for now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,10 +174,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool)
|
||||||
let triple = &sess.target.target.llvm_target;
|
let triple = &sess.target.target.llvm_target;
|
||||||
|
|
||||||
let triple = CString::new(triple.as_bytes()).unwrap();
|
let triple = CString::new(triple.as_bytes()).unwrap();
|
||||||
let cpu = match sess.opts.cg.target_cpu {
|
let cpu = sess.target_cpu();
|
||||||
Some(ref s) => &**s,
|
|
||||||
None => &*sess.target.target.options.cpu
|
|
||||||
};
|
|
||||||
let cpu = CString::new(cpu.as_bytes()).unwrap();
|
let cpu = CString::new(cpu.as_bytes()).unwrap();
|
||||||
let features = attributes::llvm_target_features(sess)
|
let features = attributes::llvm_target_features(sess)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
@ -294,7 +291,7 @@ impl ModuleConfig {
|
||||||
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
|
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
|
||||||
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 ||
|
||||||
sess.opts.debugging_opts.cross_lang_lto;
|
sess.opts.debugging_opts.cross_lang_lto.embed_bitcode();
|
||||||
if embed_bitcode {
|
if embed_bitcode {
|
||||||
match sess.opts.optimize {
|
match sess.opts.optimize {
|
||||||
config::OptLevel::No |
|
config::OptLevel::No |
|
||||||
|
@ -1358,7 +1355,8 @@ fn execute_work_item(cgcx: &CodegenContext,
|
||||||
|
|
||||||
// Don't run LTO passes when cross-lang LTO is enabled. The linker
|
// Don't run LTO passes when cross-lang LTO is enabled. The linker
|
||||||
// will do that for us in this case.
|
// will do that for us in this case.
|
||||||
let needs_lto = needs_lto && !cgcx.opts.debugging_opts.cross_lang_lto;
|
let needs_lto = needs_lto &&
|
||||||
|
!cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode();
|
||||||
|
|
||||||
if needs_lto {
|
if needs_lto {
|
||||||
Ok(WorkItemResult::NeedsLTO(mtrans))
|
Ok(WorkItemResult::NeedsLTO(mtrans))
|
||||||
|
|
|
@ -18,9 +18,9 @@ endif
|
||||||
OBJDUMP=llvm-objdump
|
OBJDUMP=llvm-objdump
|
||||||
SECTION_HEADERS=$(OBJDUMP) -section-headers
|
SECTION_HEADERS=$(OBJDUMP) -section-headers
|
||||||
|
|
||||||
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1
|
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto=no-link -Ccodegen-units=1
|
||||||
|
|
||||||
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1 --emit=obj
|
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto=no-link -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue