rustc: Add -C lto=val option
This commit primarily adds the ability to control what kind of LTO happens when rustc performs LTO, namely allowing values to be specified to the `-C lto` option, such as `-C lto=thin` and `-C lto=fat`. (where "fat" is the previous kind of LTO, throw everything in one giant module) Along the way this also refactors a number of fields which store information about whether LTO/ThinLTO are enabled to unify them all into one field through which everything is dispatched, hopefully removing a number of special cases throughout. This is intended to help mitigate #47409 but will require a backport as well, and this would unfortunately need to be an otherwise insta-stable option.
This commit is contained in:
parent
4e3901d35f
commit
8bde2acfc7
9 changed files with 222 additions and 116 deletions
|
|
@ -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,
|
||||
|
|
@ -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],
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
) }
|
||||
|
||||
|
|
@ -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)"),
|
||||
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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};
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
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 {
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue