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,
// 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],
}
);
debug_assertions: true,
actually_rustdoc: false,
cli_forced_codegen_units: None,
- cli_forced_thinlto: None,
+ cli_forced_thinlto_off: false,
}
}
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;
_ => 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
+ }
}
) }
"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)"),
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.
}
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;
}
}
}
(&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");
}
debug_assertions,
actually_rustdoc: false,
cli_forced_codegen_units: codegen_units,
- cli_forced_thinlto: thinlto,
+ cli_forced_thinlto_off: disable_thinlto,
},
cfg)
}
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};
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);
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;
// 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();
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 {
// 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,
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;
});
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());
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));
// 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));
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
e));
}
}
+
+fn is_full_lto_enabled(sess: &Session) -> bool {
+ match sess.lto() {
+ Lto::Yes |
+ Lto::Thin |
+ Lto::Fat => true,
+ Lto::No |
+ Lto::ThinLocal => false,
+ }
+}
use llvm;
use rustc::hir::def_id::LOCAL_CRATE;
use rustc::middle::exported_symbols::SymbolExportLevel;
-use rustc::session::config;
+use rustc::session::config::{self, Lto};
use rustc::util::common::time;
use time_graph::Timeline;
use {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource};
}
}
-pub enum LTOMode {
- WholeCrateGraph,
- JustThisCrate,
-}
-
pub(crate) fn run(cgcx: &CodegenContext,
- modules: Vec<ModuleTranslation>,
- mode: LTOMode,
- timeline: &mut Timeline)
+ modules: Vec<ModuleTranslation>,
+ timeline: &mut Timeline)
-> Result<Vec<LtoModuleTranslation>, FatalError>
{
let diag_handler = cgcx.create_diag_handler();
- let export_threshold = match mode {
- LTOMode::WholeCrateGraph => {
+ let export_threshold = match cgcx.lto {
+ // We're just doing LTO for our one crate
+ Lto::ThinLocal => SymbolExportLevel::Rust,
+
+ // We're doing LTO for the entire crate graph
+ Lto::Yes | Lto::Fat | Lto::Thin => {
symbol_export::crates_export_threshold(&cgcx.crate_types)
}
- LTOMode::JustThisCrate => {
- SymbolExportLevel::Rust
- }
+
+ Lto::No => panic!("didn't request LTO but we're doing LTO"),
};
let symbol_filter = &|&(ref name, _, level): &(String, _, SymbolExportLevel)| {
// We save off all the bytecode and LLVM module ids for later processing
// with either fat or thin LTO
let mut upstream_modules = Vec::new();
- if let LTOMode::WholeCrateGraph = mode {
+ if cgcx.lto != Lto::ThinLocal {
if cgcx.opts.cg.prefer_dynamic {
diag_handler.struct_err("cannot prefer dynamic linking when performing LTO")
.note("only 'staticlib', 'bin', and 'cdylib' outputs are \
}
let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
- match mode {
- LTOMode::WholeCrateGraph if !cgcx.thinlto => {
+ match cgcx.lto {
+ Lto::Yes | // `-C lto` == fat LTO by default
+ Lto::Fat => {
fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline)
}
- _ => {
+ Lto::Thin |
+ Lto::ThinLocal => {
thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline)
}
+ Lto::No => unreachable!(),
}
}
use rustc::dep_graph::{DepGraph, WorkProductFileKind};
use rustc::middle::cstore::{LinkMeta, EncodedMetadata};
use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses,
- AllPasses, Sanitizer};
+ AllPasses, Sanitizer, Lto};
use rustc::session::Session;
use rustc::util::nodemap::FxHashMap;
use rustc_back::LinkerFlavor;
pub struct CodegenContext {
// Resouces needed when running LTO
pub time_passes: bool,
- pub lto: bool,
- pub thinlto: bool,
+ pub lto: Lto,
pub no_landing_pads: bool,
pub save_temps: bool,
pub fewer_names: bool,
TRANS_WORK_PACKAGE_KIND,
"generate lto")
}).unwrap_or(Timeline::noop());
- let mode = if cgcx.lto {
- lto::LTOMode::WholeCrateGraph
- } else {
- lto::LTOMode::JustThisCrate
- };
- let lto_modules = lto::run(cgcx, modules, mode, &mut timeline)
+ let lto_modules = lto::run(cgcx, modules, &mut timeline)
.unwrap_or_else(|e| panic!(e));
lto_modules.into_iter().map(|module| {
unsafe {
optimize(cgcx, &diag_handler, &mtrans, config, timeline)?;
- let lto = cgcx.lto;
+ // After we've done the initial round of optimizations we need to
+ // decide whether to synchronously codegen this module or ship it
+ // back to the coordinator thread for further LTO processing (which
+ // has to wait for all the initial modules to be optimized).
+ //
+ // Here we dispatch based on the `cgcx.lto` and kind of module we're
+ // translating...
+ let needs_lto = match cgcx.lto {
+ Lto::No => false,
+
+ // Here we've got a full crate graph LTO requested. We ignore
+ // this, however, if the crate type is only an rlib as there's
+ // no full crate graph to process, that'll happen later.
+ //
+ // This use case currently comes up primarily for targets that
+ // require LTO so the request for LTO is always unconditionally
+ // passed down to the backend, but we don't actually want to do
+ // anything about it yet until we've got a final product.
+ Lto::Yes | Lto::Fat | Lto::Thin => {
+ cgcx.crate_types.len() != 1 ||
+ cgcx.crate_types[0] != config::CrateTypeRlib
+ }
- let auto_thin_lto =
- cgcx.thinlto &&
- cgcx.total_cgus > 1 &&
- mtrans.kind != ModuleKind::Allocator;
+ // When we're automatically doing ThinLTO for multi-codegen-unit
+ // builds we don't actually want to LTO the allocator modules if
+ // it shows up. This is due to various linker shenanigans that
+ // we'll encounter later.
+ //
+ // Additionally here's where we also factor in the current LLVM
+ // version. If it doesn't support ThinLTO we skip this.
+ Lto::ThinLocal => {
+ mtrans.kind != ModuleKind::Allocator &&
+ llvm::LLVMRustThinLTOAvailable()
+ }
+ };
- // If we're a metadata module we never participate in LTO.
- //
- // If LTO was explicitly requested on the command line, we always
- // LTO everything else.
- //
- // If LTO *wasn't* explicitly requested and we're not a metdata
- // module, then we may automatically do ThinLTO if we've got
- // multiple codegen units. Note, however, that the allocator module
- // doesn't participate here automatically because of linker
- // shenanigans later on.
- if mtrans.kind == ModuleKind::Metadata || (!lto && !auto_thin_lto) {
+ // Metadata modules never participate in LTO regardless of the lto
+ // settings.
+ let needs_lto = needs_lto && mtrans.kind != ModuleKind::Metadata;
+
+ if needs_lto {
+ Ok(WorkItemResult::NeedsLTO(mtrans))
+ } else {
let module = codegen(cgcx, &diag_handler, mtrans, config, timeline)?;
Ok(WorkItemResult::Compiled(module))
- } else {
- Ok(WorkItemResult::NeedsLTO(mtrans))
}
}
}
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
}));
- let crate_types = sess.crate_types.borrow();
- let only_rlib = crate_types.len() == 1 &&
- crate_types[0] == config::CrateTypeRlib;
-
let wasm_import_memory =
attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory");
let cgcx = CodegenContext {
crate_types: sess.crate_types.borrow().clone(),
each_linked_rlib_for_lto,
- // If we're only building an rlibc then allow the LTO flag to be passed
- // but don't actually do anything, the full LTO will happen later
- lto: sess.lto() && !only_rlib,
-
- // Enable ThinLTO if requested, but only if the target we're compiling
- // for doesn't require full LTO. Some targets require one LLVM module
- // (they effectively don't have a linker) so it's up to us to use LTO to
- // link everything together.
- thinlto: sess.thinlto() &&
- !sess.target.target.options.requires_lto &&
- unsafe { llvm::LLVMRustThinLTOAvailable() },
-
+ lto: sess.lto(),
no_landing_pads: sess.no_landing_pads(),
fewer_names: sess.fewer_names(),
save_temps: sess.opts.cg.save_temps,
$(RUSTC) -C extra-filename=foo dummy.rs 2>&1
#Option taking no argument
$(RUSTC) -C lto= dummy.rs 2>&1 | \
- $(CGREP) 'codegen option `lto` takes no value'
+ $(CGREP) 'codegen option `lto` - one of `thin`, `fat`, or'
$(RUSTC) -C lto=1 dummy.rs 2>&1 | \
- $(CGREP) 'codegen option `lto` takes no value'
+ $(CGREP) 'codegen option `lto` - one of `thin`, `fat`, or'
$(RUSTC) -C lto=foo dummy.rs 2>&1 | \
- $(CGREP) 'codegen option `lto` takes no value'
+ $(CGREP) 'codegen option `lto` - one of `thin`, `fat`, or'
$(RUSTC) -C lto dummy.rs
# Should not link dead code...
--- /dev/null
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Clto=fat
+// no-prefer-dynamic
+
+fn main() {
+ println!("hello!");
+}
+
--- /dev/null
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Clto=thin
+// no-prefer-dynamic
+// min-llvm-version 4.0
+
+fn main() {
+ println!("hello!");
+}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// compile-flags: -Z thinlto -C codegen-units=8 -O -C lto
+// compile-flags: -C codegen-units=8 -O -C lto=thin
// aux-build:thin-lto-inlines-aux.rs
// min-llvm-version 4.0
// no-prefer-dynamic