Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc: Add -C lto=val option #47521

Merged
merged 1 commit into from
Jan 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 45 additions & 11 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,26 @@ pub enum OptLevel {
SizeMin, // -Oz
}

#[derive(Clone, Copy, PartialEq, Hash)]
pub enum Lto {
/// Don't do any LTO whatsoever
No,

/// Do a full crate graph LTO. The flavor is determined by the compiler
/// (currently the default is "fat").
Yes,

/// Do a full crate graph LTO with ThinLTO
Thin,

/// Do a local graph LTO with ThinLTO (only relevant for multiple codegen
/// units).
ThinLocal,

/// Do a full crate graph LTO with "fat" LTO
Fat,
}

#[derive(Clone, Copy, PartialEq, Hash)]
pub enum DebugInfoLevel {
NoDebugInfo,
Expand Down Expand Up @@ -389,7 +409,7 @@ top_level_options!(
// commands like `--emit llvm-ir` which they're often incompatible with
// if we otherwise use the defaults of rustc.
cli_forced_codegen_units: Option<usize> [UNTRACKED],
cli_forced_thinlto: Option<bool> [UNTRACKED],
cli_forced_thinlto_off: bool [UNTRACKED],
}
);

Expand Down Expand Up @@ -590,7 +610,7 @@ pub fn basic_options() -> Options {
debug_assertions: true,
actually_rustdoc: false,
cli_forced_codegen_units: None,
cli_forced_thinlto: None,
cli_forced_thinlto_off: false,
}
}

Expand Down Expand Up @@ -780,11 +800,13 @@ macro_rules! options {
Some("crate=integer");
pub const parse_unpretty: Option<&'static str> =
Some("`string` or `string=string`");
pub const parse_lto: Option<&'static str> =
Some("one of `thin`, `fat`, or omitted");
}

#[allow(dead_code)]
mod $mod_set {
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer};
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer, Lto};
use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel};
use std::path::PathBuf;

Expand Down Expand Up @@ -978,6 +1000,16 @@ macro_rules! options {
_ => false,
}
}

fn parse_lto(slot: &mut Lto, v: Option<&str>) -> bool {
*slot = match v {
None => Lto::Yes,
Some("thin") => Lto::Thin,
Some("fat") => Lto::Fat,
Some(_) => return false,
};
true
}
}
) }

Expand All @@ -994,7 +1026,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
"extra arguments to append to the linker invocation (space separated)"),
link_dead_code: bool = (false, parse_bool, [UNTRACKED],
"don't let linker strip dead code (turning it on can be used for code coverage)"),
lto: bool = (false, parse_bool, [TRACKED],
lto: Lto = (Lto::No, parse_lto, [TRACKED],
"perform LLVM link-time optimizations"),
target_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
"select target processor (rustc --print target-cpus for details)"),
Expand Down Expand Up @@ -1677,7 +1709,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)

let mut cg = build_codegen_options(matches, error_format);
let mut codegen_units = cg.codegen_units;
let mut thinlto = None;
let mut disable_thinlto = false;

// Issue #30063: if user requests llvm-related output to one
// particular path, disable codegen-units.
Expand All @@ -1699,12 +1731,12 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
}
early_warn(error_format, "resetting to default -C codegen-units=1");
codegen_units = Some(1);
thinlto = Some(false);
disable_thinlto = true;
}
}
_ => {
codegen_units = Some(1);
thinlto = Some(false);
disable_thinlto = true;
}
}
}
Expand Down Expand Up @@ -1734,7 +1766,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
(&None, &None) => None,
}.map(|m| PathBuf::from(m));

if cg.lto && incremental.is_some() {
if cg.lto != Lto::No && incremental.is_some() {
early_error(error_format, "can't perform LTO when compiling incrementally");
}

Expand Down Expand Up @@ -1934,7 +1966,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
debug_assertions,
actually_rustdoc: false,
cli_forced_codegen_units: codegen_units,
cli_forced_thinlto: thinlto,
cli_forced_thinlto_off: disable_thinlto,
},
cfg)
}
Expand Down Expand Up @@ -2052,7 +2084,7 @@ mod dep_tracking {
use std::hash::Hash;
use std::path::PathBuf;
use std::collections::hash_map::DefaultHasher;
use super::{Passes, CrateType, OptLevel, DebugInfoLevel,
use super::{Passes, CrateType, OptLevel, DebugInfoLevel, Lto,
OutputTypes, Externs, ErrorOutputType, Sanitizer};
use syntax::feature_gate::UnstableFeatures;
use rustc_back::{PanicStrategy, RelroLevel};
Expand Down Expand Up @@ -2107,6 +2139,7 @@ mod dep_tracking {
impl_dep_tracking_hash_via_hash!(RelroLevel);
impl_dep_tracking_hash_via_hash!(Passes);
impl_dep_tracking_hash_via_hash!(OptLevel);
impl_dep_tracking_hash_via_hash!(Lto);
impl_dep_tracking_hash_via_hash!(DebugInfoLevel);
impl_dep_tracking_hash_via_hash!(UnstableFeatures);
impl_dep_tracking_hash_via_hash!(Externs);
Expand Down Expand Up @@ -2180,6 +2213,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::build_session;
use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;
Expand Down Expand Up @@ -2656,7 +2690,7 @@ mod tests {

// Make sure changing a [TRACKED] option changes the hash
opts = reference.clone();
opts.cg.lto = true;
opts.cg.lto = Lto::Fat;
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());

opts = reference.clone();
Expand Down
92 changes: 58 additions & 34 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,65 @@ impl Session {
self.use_mir()
}

pub fn lto(&self) -> bool {
self.opts.cg.lto || self.target.target.options.requires_lto
/// Calculates the flavor of LTO to use for this compilation.
pub fn lto(&self) -> config::Lto {
// If our target has codegen requirements ignore the command line
if self.target.target.options.requires_lto {
return config::Lto::Fat
}

// If the user specified something, return that. If they only said `-C
// lto` and we've for whatever reason forced off ThinLTO via the CLI,
// then ensure we can't use a ThinLTO.
match self.opts.cg.lto {
config::Lto::No => {}
config::Lto::Yes if self.opts.cli_forced_thinlto_off => {
return config::Lto::Fat
}
other => return other,
}

// Ok at this point the target doesn't require anything and the user
// hasn't asked for anything. Our next decision is whether or not
// we enable "auto" ThinLTO where we use multiple codegen units and
// then do ThinLTO over those codegen units. The logic below will
// either return `No` or `ThinLocal`.

// If processing command line options determined that we're incompatible
// with ThinLTO (e.g. `-C lto --emit llvm-ir`) then return that option.
if self.opts.cli_forced_thinlto_off {
return config::Lto::No
}

// If `-Z thinlto` specified process that, but note that this is mostly
// a deprecated option now that `-C lto=thin` exists.
if let Some(enabled) = self.opts.debugging_opts.thinlto {
if enabled {
return config::Lto::ThinLocal
} else {
return config::Lto::No
}
}

// If there's only one codegen unit and LTO isn't enabled then there's
// no need for ThinLTO so just return false.
if self.codegen_units() == 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, so I sort of feel like this logic should be in trans somewhere instead of here. Though it doesn't really matter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it would make sense to move that to trans at some point because we only really know after CGU partitioning how many object files there are. At moment it's likely that there's always more than one though.

return config::Lto::No
}

// Right now ThinLTO isn't compatible with incremental compilation.
if self.opts.incremental.is_some() {
return config::Lto::No
}

// Now we're in "defaults" territory. By default we enable ThinLTO for
// optimized compiles (anything greater than O0).
match self.opts.optimize {
config::OptLevel::No => config::Lto::No,
_ => config::Lto::ThinLocal,
}
}

/// Returns the panic strategy for this compile session. If the user explicitly selected one
/// using '-C panic', use that, otherwise use the panic strategy defined by the target.
pub fn panic_strategy(&self) -> PanicStrategy {
Expand Down Expand Up @@ -804,38 +860,6 @@ impl Session {
// scientific.
16
}

/// Returns whether ThinLTO is enabled for this compilation
pub fn thinlto(&self) -> bool {
// If processing command line options determined that we're incompatible
// with ThinLTO (e.g. `-C lto --emit llvm-ir`) then return that option.
if let Some(enabled) = self.opts.cli_forced_thinlto {
return enabled
}

// If explicitly specified, use that with the next highest priority
if let Some(enabled) = self.opts.debugging_opts.thinlto {
return enabled
}

// If there's only one codegen unit and LTO isn't enabled then there's
// no need for ThinLTO so just return false.
if self.codegen_units() == 1 && !self.lto() {
return false
}

// Right now ThinLTO isn't compatible with incremental compilation.
if self.opts.incremental.is_some() {
return false
}

// Now we're in "defaults" territory. By default we enable ThinLTO for
// optimized compiles (anything greater than O0).
match self.opts.optimize {
config::OptLevel::No => false,
_ => true,
}
}
}

pub fn build_session(sopts: config::Options,
Expand Down
22 changes: 17 additions & 5 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::rpath::RPathConfig;
use super::rpath;
use metadata::METADATA_FILENAME;
use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest};
use rustc::session::config::RUST_CGU_EXT;
use rustc::session::config::{RUST_CGU_EXT, Lto};
use rustc::session::filesearch;
use rustc::session::search_paths::PathKind;
use rustc::session::Session;
Expand Down Expand Up @@ -503,7 +503,8 @@ fn link_staticlib(sess: &Session,
});
ab.add_rlib(path,
&name.as_str(),
sess.lto() && !ignored_for_lto(sess, &trans.crate_info, cnum),
is_full_lto_enabled(sess) &&
!ignored_for_lto(sess, &trans.crate_info, cnum),
skip_object_files).unwrap();

all_native_libs.extend(trans.crate_info.native_libraries[&cnum].iter().cloned());
Expand Down Expand Up @@ -1211,7 +1212,8 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib)
});

if (!sess.lto() || ignored_for_lto(sess, &trans.crate_info, cnum)) &&
if (!is_full_lto_enabled(sess) ||
ignored_for_lto(sess, &trans.crate_info, cnum)) &&
crate_type != config::CrateTypeDylib &&
!skip_native {
cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
Expand Down Expand Up @@ -1264,7 +1266,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
// file, then we don't need the object file as it's part of the
// LTO module. Note that `#![no_builtins]` is excluded from LTO,
// though, so we let that object file slide.
let skip_because_lto = sess.lto() &&
let skip_because_lto = is_full_lto_enabled(sess) &&
is_rust_object &&
(sess.target.target.options.no_builtins ||
!trans.crate_info.is_no_builtins.contains(&cnum));
Expand Down Expand Up @@ -1301,7 +1303,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
fn add_dynamic_crate(cmd: &mut Linker, sess: &Session, cratepath: &Path) {
// If we're performing LTO, then it should have been previously required
// that all upstream rust dependencies were available in an rlib format.
assert!(!sess.lto());
assert!(!is_full_lto_enabled(sess));

// Just need to tell the linker about where the library lives and
// what its name is
Expand Down Expand Up @@ -1409,3 +1411,13 @@ fn link_binaryen(sess: &Session,
e));
}
}

fn is_full_lto_enabled(sess: &Session) -> bool {
match sess.lto() {
Lto::Yes |
Lto::Thin |
Lto::Fat => true,
Lto::No |
Lto::ThinLocal => false,
}
}
Loading