about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-01-16 15:02:31 -0800
committerAlex Crichton <alex@alexcrichton.com>2018-01-23 14:13:47 -0800
commit8bde2acfc7876148ebfe192780434dbb969eded3 (patch)
tree214b953ac0b69b6185e8de690f7c4f8588287f95 /src
parent4e3901d35f6a8652f67111e7272263c9e62ab3e1 (diff)
downloadrust-8bde2acfc7876148ebfe192780434dbb969eded3.tar.gz
rust-8bde2acfc7876148ebfe192780434dbb969eded3.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/session/config.rs56
-rw-r--r--src/librustc/session/mod.rs92
-rw-r--r--src/librustc_trans/back/link.rs22
-rw-r--r--src/librustc_trans/back/lto.rs36
-rw-r--r--src/librustc_trans/back/write.rs88
-rw-r--r--src/test/run-make/codegen-options-parsing/Makefile6
-rw-r--r--src/test/run-pass/fat-lto.rs17
-rw-r--r--src/test/run-pass/thinlto/all-crates.rs17
-rw-r--r--src/test/run-pass/thinlto/thin-lto-inlines2.rs2
9 files changed, 221 insertions, 115 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index da119ba4569..3fac9ce41f1 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -73,6 +73,26 @@ pub enum OptLevel {
 }
 
 #[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,
     LimitedDebugInfo,
@@ -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();
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 36f716a4a76..02cd6a92eb7 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -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,
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 923e5549927..f050edcd513 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -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;
@@ -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());
@@ -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));
@@ -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));
@@ -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
@@ -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,
+    }
+}
diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs
index b612247ffcd..9ff5bcf7a33 100644
--- a/src/librustc_trans/back/lto.rs
+++ b/src/librustc_trans/back/lto.rs
@@ -18,7 +18,7 @@ use llvm::{ModuleRef, TargetMachineRef, True, 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};
@@ -95,25 +95,22 @@ impl LtoModuleTranslation {
     }
 }
 
-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)| {
@@ -140,7 +137,7 @@ pub(crate) fn run(cgcx: &CodegenContext,
     // 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 \
@@ -186,13 +183,16 @@ pub(crate) fn run(cgcx: &CodegenContext,
     }
 
     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!(),
     }
 }
 
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index a013af7a460..a4af080ebf7 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -19,7 +19,7 @@ use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
 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;
@@ -318,8 +318,7 @@ impl ModuleConfig {
 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,
@@ -576,12 +575,7 @@ fn generate_lto_work(cgcx: &CodegenContext,
                  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| {
@@ -1280,28 +1274,51 @@ fn execute_work_item(cgcx: &CodegenContext,
         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))
             }
         }
     }
@@ -1377,28 +1394,13 @@ fn start_executing_work(tcx: TyCtxt,
         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,
diff --git a/src/test/run-make/codegen-options-parsing/Makefile b/src/test/run-make/codegen-options-parsing/Makefile
index 81e06043c87..fda96a8b1fb 100644
--- a/src/test/run-make/codegen-options-parsing/Makefile
+++ b/src/test/run-make/codegen-options-parsing/Makefile
@@ -16,11 +16,11 @@ all:
 	$(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...
diff --git a/src/test/run-pass/fat-lto.rs b/src/test/run-pass/fat-lto.rs
new file mode 100644
index 00000000000..453eede261c
--- /dev/null
+++ b/src/test/run-pass/fat-lto.rs
@@ -0,0 +1,17 @@
+// 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!");
+}
+
diff --git a/src/test/run-pass/thinlto/all-crates.rs b/src/test/run-pass/thinlto/all-crates.rs
new file mode 100644
index 00000000000..772a9ec8293
--- /dev/null
+++ b/src/test/run-pass/thinlto/all-crates.rs
@@ -0,0 +1,17 @@
+// 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!");
+}
diff --git a/src/test/run-pass/thinlto/thin-lto-inlines2.rs b/src/test/run-pass/thinlto/thin-lto-inlines2.rs
index 0e8ad08a5f6..6020f72415d 100644
--- a/src/test/run-pass/thinlto/thin-lto-inlines2.rs
+++ b/src/test/run-pass/thinlto/thin-lto-inlines2.rs
@@ -8,7 +8,7 @@
 // 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