Skip to content

Commit

Permalink
Allow for specifying a linker plugin for cross-language LTO
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelwoerister committed May 9, 2018
1 parent 8ff4b42 commit a981089
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 13 deletions.
55 changes: 51 additions & 4 deletions src/librustc/session/config.rs
Expand Up @@ -95,6 +95,23 @@ pub enum Lto {
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)]
pub enum DebugInfoLevel {
NoDebugInfo,
Expand Down Expand Up @@ -412,6 +429,7 @@ top_level_options!(

// Remap source path prefixes in all output (messages, object files, debug, etc)
remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED],

edition: Edition [TRACKED],
}
);
Expand Down Expand Up @@ -777,11 +795,15 @@ macro_rules! options {
Some("`string` or `string=string`");
pub const parse_lto: Option<&'static str> =
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)]
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 std::path::PathBuf;

Expand Down Expand Up @@ -986,6 +1008,26 @@ macro_rules! options {
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
}
}
) }

Expand Down Expand Up @@ -1295,7 +1337,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"make the current crate share its generic instantiations"),
chalk: bool = (false, parse_bool, [TRACKED],
"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."),
}

Expand Down Expand Up @@ -2327,7 +2369,7 @@ mod dep_tracking {
use std::path::PathBuf;
use std::collections::hash_map::DefaultHasher;
use super::{CrateType, DebugInfoLevel, ErrorOutputType, Lto, OptLevel, OutputTypes,
Passes, Sanitizer};
Passes, Sanitizer, CrossLangLto};
use syntax::feature_gate::UnstableFeatures;
use rustc_target::spec::{PanicStrategy, RelroLevel, TargetTriple};
use syntax::edition::Edition;
Expand Down Expand Up @@ -2391,6 +2433,7 @@ mod dep_tracking {
impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
impl_dep_tracking_hash_via_hash!(TargetTriple);
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!(PathBuf);
Expand Down Expand Up @@ -2455,7 +2498,7 @@ mod tests {
use lint;
use middle::cstore;
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 std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;
Expand Down Expand Up @@ -3111,6 +3154,10 @@ mod tests {
opts = reference.clone();
opts.debugging_opts.relro_level = Some(RelroLevel::Full);
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]
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/session/mod.rs
Expand Up @@ -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 {
if let Some(x) = self.opts.cg.force_frame_pointers {
x
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_trans/back/link.rs
Expand Up @@ -970,6 +970,9 @@ fn link_args(cmd: &mut Linker,
out_filename: &Path,
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 location of crates will be determined as needed.
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
Expand Down
52 changes: 51 additions & 1 deletion src/librustc_trans/back/linker.rs
Expand Up @@ -21,7 +21,8 @@ use back::symbol_export;
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
use rustc::middle::dependency_format::Linkage;
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_target::spec::{LinkerFlavor, LldFlavor};
use serialize::{json, Encoder};
Expand Down Expand Up @@ -127,6 +128,7 @@ pub trait Linker {
fn subsystem(&mut self, subsystem: &str);
fn group_start(&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?).
fn finalize(&mut self) -> Command;
}
Expand Down Expand Up @@ -434,6 +436,42 @@ impl<'a> Linker for GccLinker<'a> {
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> {
Expand Down Expand Up @@ -666,6 +704,10 @@ impl<'a> Linker for MsvcLinker<'a> {
// MSVC doesn't need group indicators
fn group_start(&mut self) {}
fn group_end(&mut self) {}

fn cross_lang_lto(&mut self) {
// Do nothing
}
}

pub struct EmLinker<'a> {
Expand Down Expand Up @@ -832,6 +874,10 @@ impl<'a> Linker for EmLinker<'a> {
// Appears not necessary on Emscripten
fn group_start(&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> {
Expand Down Expand Up @@ -984,4 +1030,8 @@ impl Linker for WasmLd {
// Not needed for now with LLD
fn group_start(&mut self) {}
fn group_end(&mut self) {}

fn cross_lang_lto(&mut self) {
// Do nothing for now
}
}
10 changes: 4 additions & 6 deletions src/librustc_trans/back/write.rs
Expand Up @@ -174,10 +174,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool)
let triple = &sess.target.target.llvm_target;

let triple = CString::new(triple.as_bytes()).unwrap();
let cpu = match sess.opts.cg.target_cpu {
Some(ref s) => &**s,
None => &*sess.target.target.options.cpu
};
let cpu = sess.target_cpu();
let cpu = CString::new(cpu.as_bytes()).unwrap();
let features = attributes::llvm_target_features(sess)
.collect::<Vec<_>>()
Expand Down Expand Up @@ -294,7 +291,7 @@ impl ModuleConfig {
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
let embed_bitcode = sess.target.target.options.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 {
match sess.opts.optimize {
config::OptLevel::No |
Expand Down Expand Up @@ -1358,7 +1355,8 @@ fn execute_work_item(cgcx: &CodegenContext,

// Don't run LTO passes when cross-lang LTO is enabled. The linker
// 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 {
Ok(WorkItemResult::NeedsLTO(mtrans))
Expand Down
4 changes: 2 additions & 2 deletions src/test/run-make/cross-lang-lto/Makefile
Expand Up @@ -18,9 +18,9 @@ endif
OBJDUMP=llvm-objdump
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

Expand Down

0 comments on commit a981089

Please sign in to comment.