about summary refs log tree commit diff
path: root/src/librustc_trans/back/write.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_trans/back/write.rs')
-rw-r--r--src/librustc_trans/back/write.rs821
1 files changed, 595 insertions, 226 deletions
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index ef6bf2504f3..cb883e0349f 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -8,43 +8,48 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use back::lto;
+use back::bytecode::{self, RLIB_BYTECODE_EXTENSION};
+use back::lto::{self, ModuleBuffer, ThinBuffer};
 use back::link::{self, get_linker, remove};
 use back::linker::LinkerInfo;
 use back::symbol_export::ExportedSymbols;
+use base;
+use consts;
 use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
-use rustc::dep_graph::DepGraph;
+use rustc::dep_graph::{DepGraph, WorkProductFileKind};
 use rustc::middle::cstore::{LinkMeta, EncodedMetadata};
 use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses,
                              AllPasses, Sanitizer};
 use rustc::session::Session;
 use rustc::util::nodemap::FxHashMap;
-use time_graph::{self, TimeGraph};
+use rustc_back::LinkerFlavor;
+use time_graph::{self, TimeGraph, Timeline};
 use llvm;
 use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef};
-use llvm::SMDiagnosticRef;
+use llvm::{SMDiagnosticRef, ContextRef};
 use {CrateTranslation, ModuleSource, ModuleTranslation, CompiledModule, ModuleKind};
 use CrateInfo;
 use rustc::hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc::ty::TyCtxt;
 use rustc::util::common::{time, time_depth, set_time_depth, path2cstr, print_time_passes_entry};
 use rustc::util::fs::{link_or_copy, rename_or_copy_remove};
-use errors::{self, Handler, Level, DiagnosticBuilder, FatalError};
+use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId};
 use errors::emitter::{Emitter};
 use syntax::attr;
 use syntax::ext::hygiene::Mark;
 use syntax_pos::MultiSpan;
 use syntax_pos::symbol::Symbol;
+use type_::Type;
 use context::{is_pie_binary, get_reloc_model};
 use jobserver::{Client, Acquired};
 use rustc_demangle;
 
 use std::any::Any;
-use std::ffi::CString;
-use std::fmt;
-use std::fs;
+use std::ffi::{CString, CStr};
+use std::fs::{self, File};
 use std::io;
-use std::io::Write;
+use std::io::{Read, Write};
+use std::mem;
 use std::path::{Path, PathBuf};
 use std::str;
 use std::sync::Arc;
@@ -72,6 +77,13 @@ pub const CODE_GEN_MODEL_ARGS : [(&'static str, llvm::CodeModel); 5] = [
     ("large", llvm::CodeModel::Large),
 ];
 
+pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [
+    ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic),
+    ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic),
+    ("initial-exec", llvm::ThreadLocalMode::InitialExec),
+    ("local-exec", llvm::ThreadLocalMode::LocalExec),
+];
+
 pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError {
     match llvm::last_error() {
         Some(err) => handler.fatal(&format!("{}: {}", msg, err)),
@@ -143,6 +155,14 @@ fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize {
 }
 
 pub fn create_target_machine(sess: &Session) -> TargetMachineRef {
+    target_machine_factory(sess)().unwrap_or_else(|err| {
+        panic!(llvm_err(sess.diagnostic(), err))
+    })
+}
+
+pub fn target_machine_factory(sess: &Session)
+    -> Arc<Fn() -> Result<TargetMachineRef, String> + Send + Sync>
+{
     let reloc_model = get_reloc_model(sess);
 
     let opt_level = get_llvm_opt_level(sess.opts.optimize);
@@ -161,53 +181,58 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef {
         Some(x) => x.1,
         _ => {
             sess.err(&format!("{:?} is not a valid code model",
-                             sess.opts
-                                 .cg
-                                 .code_model));
+                              code_model_arg));
             sess.abort_if_errors();
             bug!();
         }
     };
 
+    let singlethread = sess.target.target.options.singlethread;
+
     let triple = &sess.target.target.llvm_target;
 
-    let tm = unsafe {
-        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 = CString::new(cpu.as_bytes()).unwrap();
-        let features = CString::new(target_feature(sess).as_bytes()).unwrap();
-        llvm::LLVMRustCreateTargetMachine(
-            triple.as_ptr(), cpu.as_ptr(), features.as_ptr(),
-            code_model,
-            reloc_model,
-            opt_level,
-            use_softfp,
-            is_pie_binary(sess),
-            ffunction_sections,
-            fdata_sections,
-        )
+    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 = CString::new(cpu.as_bytes()).unwrap();
+    let features = CString::new(target_feature(sess).as_bytes()).unwrap();
+    let is_pie_binary = is_pie_binary(sess);
+    let trap_unreachable = sess.target.target.options.trap_unreachable;
+
+    Arc::new(move || {
+        let tm = unsafe {
+            llvm::LLVMRustCreateTargetMachine(
+                triple.as_ptr(), cpu.as_ptr(), features.as_ptr(),
+                code_model,
+                reloc_model,
+                opt_level,
+                use_softfp,
+                is_pie_binary,
+                ffunction_sections,
+                fdata_sections,
+                trap_unreachable,
+                singlethread,
+            )
+        };
 
-    if tm.is_null() {
-        let msg = format!("Could not create LLVM TargetMachine for triple: {}",
-                          triple);
-        panic!(llvm_err(sess.diagnostic(), msg));
-    } else {
-        return tm;
-    };
+        if tm.is_null() {
+            Err(format!("Could not create LLVM TargetMachine for triple: {}",
+                        triple.to_str().unwrap()))
+        } else {
+            Ok(tm)
+        }
+    })
 }
 
-
 /// Module-specific configuration for `optimize_and_codegen`.
 pub struct ModuleConfig {
     /// Names of additional optimization passes to run.
     passes: Vec<String>,
     /// Some(level) to optimize at a certain level, or None to run
     /// absolutely no optimizations (used for the metadata module).
-    opt_level: Option<llvm::CodeGenOptLevel>,
+    pub opt_level: Option<llvm::CodeGenOptLevel>,
 
     /// Some(level) to optimize binary size, or None to not affect program size.
     opt_size: Option<llvm::CodeGenOptSize>,
@@ -215,6 +240,7 @@ pub struct ModuleConfig {
     // Flags indicating which outputs to produce.
     emit_no_opt_bc: bool,
     emit_bc: bool,
+    emit_bc_compressed: bool,
     emit_lto_bc: bool,
     emit_ir: bool,
     emit_asm: bool,
@@ -244,6 +270,7 @@ impl ModuleConfig {
 
             emit_no_opt_bc: false,
             emit_bc: false,
+            emit_bc_compressed: false,
             emit_lto_bc: false,
             emit_ir: false,
             emit_asm: false,
@@ -264,7 +291,7 @@ impl ModuleConfig {
     fn set_flags(&mut self, sess: &Session, no_builtins: bool) {
         self.no_verify = sess.no_verify();
         self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
-        self.no_builtins = no_builtins;
+        self.no_builtins = no_builtins || sess.target.target.options.no_builtins;
         self.time_passes = sess.time_passes();
         self.inline_threshold = sess.opts.cg.inline_threshold;
         self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
@@ -293,7 +320,9 @@ pub struct CodegenContext {
     // Resouces needed when running LTO
     pub time_passes: bool,
     pub lto: bool,
+    pub thinlto: bool,
     pub no_landing_pads: bool,
+    pub save_temps: bool,
     pub exported_symbols: Arc<ExportedSymbols>,
     pub opts: Arc<config::Options>,
     pub crate_types: Vec<config::CrateType>,
@@ -302,7 +331,15 @@ pub struct CodegenContext {
     regular_module_config: Arc<ModuleConfig>,
     metadata_module_config: Arc<ModuleConfig>,
     allocator_module_config: Arc<ModuleConfig>,
-
+    pub tm_factory: Arc<Fn() -> Result<TargetMachineRef, String> + Send + Sync>,
+    pub msvc_imps_needed: bool,
+    pub target_pointer_width: String,
+    binaryen_linker: bool,
+    debuginfo: config::DebugInfoLevel,
+    wasm_import_memory: bool,
+
+    // Number of cgus excluding the allocator/metadata modules
+    pub total_cgus: usize,
     // Handler to use for diagnostics produced during codegen.
     pub diag_emitter: SharedEmitter,
     // LLVM passes added by plugins.
@@ -322,22 +359,62 @@ pub struct CodegenContext {
 }
 
 impl CodegenContext {
-    fn create_diag_handler(&self) -> Handler {
+    pub fn create_diag_handler(&self) -> Handler {
         Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone()))
     }
 
-    fn config(&self, kind: ModuleKind) -> &ModuleConfig {
+    pub fn config(&self, kind: ModuleKind) -> &ModuleConfig {
         match kind {
             ModuleKind::Regular => &self.regular_module_config,
             ModuleKind::Metadata => &self.metadata_module_config,
             ModuleKind::Allocator => &self.allocator_module_config,
         }
     }
+
+    pub fn save_temp_bitcode(&self, trans: &ModuleTranslation, name: &str) {
+        if !self.save_temps {
+            return
+        }
+        unsafe {
+            let ext = format!("{}.bc", name);
+            let cgu = Some(&trans.name[..]);
+            let path = self.output_filenames.temp_path_ext(&ext, cgu);
+            let cstr = path2cstr(&path);
+            let llmod = trans.llvm().unwrap().llmod;
+            llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
+        }
+    }
+}
+
+struct DiagnosticHandlers<'a> {
+    inner: Box<(&'a CodegenContext, &'a Handler)>,
+    llcx: ContextRef,
 }
 
-struct HandlerFreeVars<'a> {
-    cgcx: &'a CodegenContext,
-    diag_handler: &'a Handler,
+impl<'a> DiagnosticHandlers<'a> {
+    fn new(cgcx: &'a CodegenContext,
+           handler: &'a Handler,
+           llcx: ContextRef) -> DiagnosticHandlers<'a> {
+        let data = Box::new((cgcx, handler));
+        unsafe {
+            let arg = &*data as &(_, _) as *const _ as *mut _;
+            llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, arg);
+            llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, arg);
+        }
+        DiagnosticHandlers {
+            inner: data,
+            llcx: llcx,
+        }
+    }
+}
+
+impl<'a> Drop for DiagnosticHandlers<'a> {
+    fn drop(&mut self) {
+        unsafe {
+            llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, 0 as *mut _);
+            llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, 0 as *mut _);
+        }
+    }
 }
 
 unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext,
@@ -349,7 +426,10 @@ unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext,
 unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef,
                                         user: *const c_void,
                                         cookie: c_uint) {
-    let HandlerFreeVars { cgcx, .. } = *(user as *const HandlerFreeVars);
+    if user.is_null() {
+        return
+    }
+    let (cgcx, _) = *(user as *const (&CodegenContext, &Handler));
 
     let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s))
         .expect("non-UTF8 SMDiagnostic");
@@ -358,7 +438,10 @@ unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef,
 }
 
 unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) {
-    let HandlerFreeVars { cgcx, diag_handler, .. } = *(user as *const HandlerFreeVars);
+    if user.is_null() {
+        return
+    }
+    let (cgcx, diag_handler) = *(user as *const (&CodegenContext, &Handler));
 
     match llvm::diagnostic::Diagnostic::unpack(info) {
         llvm::diagnostic::InlineAsm(inline) => {
@@ -389,28 +472,21 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
 }
 
 // Unsafe due to LLVM calls.
-unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
-                               diag_handler: &Handler,
-                               mtrans: ModuleTranslation,
-                               tm: TargetMachineRef,
-                               config: &ModuleConfig)
-    -> Result<CompiledModule, FatalError>
+unsafe fn optimize(cgcx: &CodegenContext,
+                   diag_handler: &Handler,
+                   mtrans: &ModuleTranslation,
+                   config: &ModuleConfig,
+                   timeline: &mut Timeline)
+    -> Result<(), FatalError>
 {
-    let (llmod, llcx) = match mtrans.source {
-        ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx),
+    let (llmod, llcx, tm) = match mtrans.source {
+        ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm),
         ModuleSource::Preexisting(_) => {
             bug!("optimize_and_codegen: called with ModuleSource::Preexisting")
         }
     };
 
-    let fv = HandlerFreeVars {
-        cgcx,
-        diag_handler,
-    };
-    let fv = &fv as *const HandlerFreeVars as *mut c_void;
-
-    llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv);
-    llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv);
+    let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
 
     let module_name = mtrans.name.clone();
     let module_name = Some(&module_name[..]);
@@ -453,7 +529,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
         if !config.no_prepopulate_passes {
             llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
             llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
-            with_llvm_pmb(llmod, &config, &mut |b| {
+            let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None);
+            with_llvm_pmb(llmod, &config, opt_level, &mut |b| {
                 llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm);
                 llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm);
             })
@@ -479,30 +556,60 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
         // Finally, run the actual optimization passes
         time(config.time_passes, &format!("llvm function passes [{}]", module_name.unwrap()), ||
              llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
+        timeline.record("fpm");
         time(config.time_passes, &format!("llvm module passes [{}]", module_name.unwrap()), ||
              llvm::LLVMRunPassManager(mpm, llmod));
 
         // Deallocate managers that we're now done with
         llvm::LLVMDisposePassManager(fpm);
         llvm::LLVMDisposePassManager(mpm);
+    }
+    Ok(())
+}
 
-        if cgcx.lto {
-            time(cgcx.time_passes, "all lto passes", || {
-                let temp_no_opt_bc_filename =
-                    cgcx.output_filenames.temp_path_ext("no-opt.lto.bc", module_name);
-                lto::run(cgcx,
-                         diag_handler,
-                         llmod,
-                         tm,
-                         &config,
-                         &temp_no_opt_bc_filename)
-            })?;
-            if config.emit_lto_bc {
-                let out = cgcx.output_filenames.temp_path_ext("lto.bc", module_name);
-                let out = path2cstr(&out);
-                llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
-            }
+fn generate_lto_work(cgcx: &CodegenContext,
+                     modules: Vec<ModuleTranslation>)
+    -> Vec<(WorkItem, u64)>
+{
+    let mut timeline = cgcx.time_graph.as_ref().map(|tg| {
+        tg.start(TRANS_WORKER_TIMELINE,
+                 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)
+        .unwrap_or_else(|e| panic!(e));
+
+    lto_modules.into_iter().map(|module| {
+        let cost = module.cost();
+        (WorkItem::LTO(module), cost)
+    }).collect()
+}
+
+unsafe fn codegen(cgcx: &CodegenContext,
+                  diag_handler: &Handler,
+                  mtrans: ModuleTranslation,
+                  config: &ModuleConfig,
+                  timeline: &mut Timeline)
+    -> Result<CompiledModule, FatalError>
+{
+    timeline.record("codegen");
+    let (llmod, llcx, tm) = match mtrans.source {
+        ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm),
+        ModuleSource::Preexisting(_) => {
+            bug!("codegen: called with ModuleSource::Preexisting")
         }
+    };
+    let module_name = mtrans.name.clone();
+    let module_name = Some(&module_name[..]);
+    let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
+
+    if cgcx.msvc_imps_needed {
+        create_msvc_imps(cgcx, llcx, llmod);
     }
 
     // A codegen-specific pass manager is used to generate object
@@ -525,21 +632,53 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
         f(cpm)
     }
 
+    // If we're going to generate wasm code from the assembly that llvm
+    // generates then we'll be transitively affecting a ton of options below.
+    // This only happens on the wasm target now.
+    let asm2wasm = cgcx.binaryen_linker &&
+        !cgcx.crate_types.contains(&config::CrateTypeRlib) &&
+        mtrans.kind == ModuleKind::Regular;
+
     // Change what we write and cleanup based on whether obj files are
     // just llvm bitcode. In that case write bitcode, and possibly
     // delete the bitcode if it wasn't requested. Don't generate the
     // machine code, instead copy the .o file from the .bc
-    let write_bc = config.emit_bc || config.obj_is_bitcode;
-    let rm_bc = !config.emit_bc && config.obj_is_bitcode;
-    let write_obj = config.emit_obj && !config.obj_is_bitcode;
-    let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode;
+    let write_bc = config.emit_bc || (config.obj_is_bitcode && !asm2wasm);
+    let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm;
+    let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm;
+    let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode && !asm2wasm;
 
     let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
     let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
 
-    if write_bc {
-        let bc_out_c = path2cstr(&bc_out);
-        llvm::LLVMWriteBitcodeToFile(llmod, bc_out_c.as_ptr());
+
+    if write_bc || config.emit_bc_compressed {
+        let thin;
+        let old;
+        let data = if llvm::LLVMRustThinLTOAvailable() {
+            thin = ThinBuffer::new(llmod);
+            thin.data()
+        } else {
+            old = ModuleBuffer::new(llmod);
+            old.data()
+        };
+        timeline.record("make-bc");
+
+        if write_bc {
+            if let Err(e) = File::create(&bc_out).and_then(|mut f| f.write_all(data)) {
+                diag_handler.err(&format!("failed to write bytecode: {}", e));
+            }
+            timeline.record("write-bc");
+        }
+
+        if config.emit_bc_compressed {
+            let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
+            let data = bytecode::encode(&mtrans.llmod_id, data);
+            if let Err(e) = File::create(&dst).and_then(|mut f| f.write_all(&data)) {
+                diag_handler.err(&format!("failed to write bytecode: {}", e));
+            }
+            timeline.record("compress-bc");
+        }
     }
 
     time(config.time_passes, &format!("codegen passes [{}]", module_name.unwrap()),
@@ -582,10 +721,11 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
             with_codegen(tm, llmod, config.no_builtins, |cpm| {
                 llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback);
                 llvm::LLVMDisposePassManager(cpm);
-            })
+            });
+            timeline.record("ir");
         }
 
-        if config.emit_asm {
+        if config.emit_asm || (asm2wasm && config.emit_obj) {
             let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
 
             // We can't use the same module for asm and binary output, because that triggers
@@ -603,13 +743,23 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
             if config.emit_obj {
                 llvm::LLVMDisposeModule(llmod);
             }
+            timeline.record("asm");
         }
 
-        if write_obj {
+        if asm2wasm && config.emit_obj {
+            let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
+            binaryen_assemble(cgcx, diag_handler, &assembly, &obj_out);
+            timeline.record("binaryen");
+
+            if !config.emit_asm {
+                drop(fs::remove_file(&assembly));
+            }
+        } else if write_obj {
             with_codegen(tm, llmod, config.no_builtins, |cpm| {
                 write_output_file(diag_handler, tm, cpm, llmod, &obj_out,
                                   llvm::FileType::ObjectFile)
             })?;
+            timeline.record("obj");
         }
 
         Ok(())
@@ -629,7 +779,49 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
         }
     }
 
-    Ok(mtrans.into_compiled_module(config.emit_obj, config.emit_bc))
+    drop(handlers);
+    Ok(mtrans.into_compiled_module(config.emit_obj,
+                                   config.emit_bc,
+                                   config.emit_bc_compressed,
+                                   &cgcx.output_filenames))
+}
+
+/// Translates the LLVM-generated `assembly` on the filesystem into a wasm
+/// module using binaryen, placing the output at `object`.
+///
+/// In this case the "object" is actually a full and complete wasm module. We
+/// won't actually be doing anything else to the output for now. This is all
+/// pretty janky and will get removed as soon as a linker for wasm exists.
+fn binaryen_assemble(cgcx: &CodegenContext,
+                     handler: &Handler,
+                     assembly: &Path,
+                     object: &Path) {
+    use rustc_binaryen::{Module, ModuleOptions};
+
+    let input = File::open(&assembly).and_then(|mut f| {
+        let mut contents = Vec::new();
+        f.read_to_end(&mut contents)?;
+        Ok(CString::new(contents)?)
+    });
+    let mut options = ModuleOptions::new();
+    if cgcx.debuginfo != config::NoDebugInfo {
+        options.debuginfo(true);
+    }
+    if cgcx.crate_types.contains(&config::CrateTypeExecutable) {
+        options.start("main");
+    }
+    options.stack(1024 * 1024);
+    options.import_memory(cgcx.wasm_import_memory);
+    let assembled = input.and_then(|input| {
+        Module::new(&input, &options)
+            .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
+    });
+    let err = assembled.and_then(|binary| {
+        File::create(&object).and_then(|mut f| f.write_all(binary.data()))
+    });
+    if let Err(e) = err {
+        handler.err(&format!("failed to run binaryen assembler: {}", e));
+    }
 }
 
 pub struct CompiledModules {
@@ -647,7 +839,8 @@ pub fn start_async_translation(tcx: TyCtxt,
                                time_graph: Option<TimeGraph>,
                                link: LinkMeta,
                                metadata: EncodedMetadata,
-                               coordinator_receive: Receiver<Box<Any + Send>>)
+                               coordinator_receive: Receiver<Box<Any + Send>>,
+                               total_cgus: usize)
                                -> OngoingCrateTranslation {
     let sess = tcx.sess;
     let crate_output = tcx.output_filenames(LOCAL_CRATE);
@@ -714,11 +907,12 @@ pub fn start_async_translation(tcx: TyCtxt,
         allocator_config.emit_bc = true;
     }
 
-    // Emit bitcode files for the crate if we're emitting an rlib.
-    // Whenever an rlib is created, the bitcode is inserted into the
-    // archive in order to allow LTO against it.
+    // Emit compressed bitcode files for the crate if we're emitting an rlib.
+    // Whenever an rlib is created, the bitcode is inserted into the archive in
+    // order to allow LTO against it.
     if need_crate_bitcode_for_rlib(sess) {
-        modules_config.emit_bc = true;
+        modules_config.emit_bc_compressed = true;
+        allocator_config.emit_bc_compressed = true;
     }
 
     for output_type in output_types_override.keys() {
@@ -771,6 +965,7 @@ pub fn start_async_translation(tcx: TyCtxt,
                                                   shared_emitter,
                                                   trans_worker_send,
                                                   coordinator_receive,
+                                                  total_cgus,
                                                   client,
                                                   time_graph.clone(),
                                                   Arc::new(modules_config),
@@ -797,8 +992,7 @@ pub fn start_async_translation(tcx: TyCtxt,
 
 fn copy_module_artifacts_into_incr_comp_cache(sess: &Session,
                                               dep_graph: &DepGraph,
-                                              compiled_modules: &CompiledModules,
-                                              crate_output: &OutputFilenames) {
+                                              compiled_modules: &CompiledModules) {
     if sess.opts.incremental.is_none() {
         return;
     }
@@ -806,21 +1000,17 @@ fn copy_module_artifacts_into_incr_comp_cache(sess: &Session,
     for module in compiled_modules.modules.iter() {
         let mut files = vec![];
 
-        if module.emit_obj {
-            let path = crate_output.temp_path(OutputType::Object, Some(&module.name));
-            files.push((OutputType::Object, path));
+        if let Some(ref path) = module.object {
+            files.push((WorkProductFileKind::Object, path.clone()));
         }
-
-        if module.emit_bc {
-            let path = crate_output.temp_path(OutputType::Bitcode, Some(&module.name));
-            files.push((OutputType::Bitcode, path));
+        if let Some(ref path) = module.bytecode {
+            files.push((WorkProductFileKind::Bytecode, path.clone()));
+        }
+        if let Some(ref path) = module.bytecode_compressed {
+            files.push((WorkProductFileKind::BytecodeCompressed, path.clone()));
         }
 
-        save_trans_partition(sess,
-                             dep_graph,
-                             &module.name,
-                             module.symbol_name_hash,
-                             &files);
+        save_trans_partition(sess, dep_graph, &module.name, &files);
     }
 }
 
@@ -924,8 +1114,6 @@ fn produce_final_output_artifacts(sess: &Session,
         // well.
 
         // Specific rules for keeping .#module-name#.bc:
-        //  - If we're building an rlib (`needs_crate_bitcode`), then keep
-        //    it.
         //  - If the user requested bitcode (`user_wants_bitcode`), and
         //    codegen_units > 1, then keep it.
         //  - If the user requested bitcode but codegen_units == 1, then we
@@ -935,41 +1123,37 @@ fn produce_final_output_artifacts(sess: &Session,
         // If you change how this works, also update back::link::link_rlib,
         // where .#module-name#.bc files are (maybe) deleted after making an
         // rlib.
-        let needs_crate_bitcode = need_crate_bitcode_for_rlib(sess);
         let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe);
 
-        let keep_numbered_bitcode = needs_crate_bitcode ||
-                (user_wants_bitcode && sess.opts.cg.codegen_units > 1);
+        let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1;
 
         let keep_numbered_objects = needs_crate_object ||
-                (user_wants_objects && sess.opts.cg.codegen_units > 1);
+                (user_wants_objects && sess.codegen_units() > 1);
 
         for module in compiled_modules.modules.iter() {
-            let module_name = Some(&module.name[..]);
-
-            if module.emit_obj && !keep_numbered_objects {
-                let path = crate_output.temp_path(OutputType::Object, module_name);
-                remove(sess, &path);
+            if let Some(ref path) = module.object {
+                if !keep_numbered_objects {
+                    remove(sess, path);
+                }
             }
 
-            if module.emit_bc && !keep_numbered_bitcode {
-                let path = crate_output.temp_path(OutputType::Bitcode, module_name);
-                remove(sess, &path);
+            if let Some(ref path) = module.bytecode {
+                if !keep_numbered_bitcode {
+                    remove(sess, path);
+                }
             }
         }
 
-        if compiled_modules.metadata_module.emit_bc && !user_wants_bitcode {
-            let path = crate_output.temp_path(OutputType::Bitcode,
-                                              Some(&compiled_modules.metadata_module.name));
-            remove(sess, &path);
-        }
-
-        if let Some(ref allocator_module) = compiled_modules.allocator_module {
-            if allocator_module.emit_bc && !user_wants_bitcode {
-                let path = crate_output.temp_path(OutputType::Bitcode,
-                                                  Some(&allocator_module.name));
+        if !user_wants_bitcode {
+            if let Some(ref path) = compiled_modules.metadata_module.bytecode {
                 remove(sess, &path);
             }
+
+            if let Some(ref allocator_module) = compiled_modules.allocator_module {
+                if let Some(ref path) = allocator_module.bytecode {
+                    remove(sess, path);
+                }
+            }
         }
     }
 
@@ -981,46 +1165,57 @@ fn produce_final_output_artifacts(sess: &Session,
 }
 
 pub fn dump_incremental_data(trans: &CrateTranslation) {
-    let mut reuse = 0;
-    for mtrans in trans.modules.iter() {
-        if mtrans.pre_existing {
-            reuse += 1;
-        }
-    }
-    eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len());
+    println!("[incremental] Re-using {} out of {} modules",
+              trans.modules.iter().filter(|m| m.pre_existing).count(),
+              trans.modules.len());
 }
 
-struct WorkItem {
-    mtrans: ModuleTranslation,
-    tm: TargetMachine,
+enum WorkItem {
+    Optimize(ModuleTranslation),
+    LTO(lto::LtoModuleTranslation),
 }
 
-impl fmt::Debug for WorkItem {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "WorkItem({})", self.mtrans.name)
+impl WorkItem {
+    fn kind(&self) -> ModuleKind {
+        match *self {
+            WorkItem::Optimize(ref m) => m.kind,
+            WorkItem::LTO(_) => ModuleKind::Regular,
+        }
     }
-}
-
-struct TargetMachine(TargetMachineRef);
 
-unsafe impl Send for TargetMachine {}
-
-impl Drop for TargetMachine {
-    fn drop(&mut self) {
-        unsafe {
-            llvm::LLVMRustDisposeTargetMachine(self.0);
+    fn name(&self) -> String {
+        match *self {
+            WorkItem::Optimize(ref m) => format!("optimize: {}", m.name),
+            WorkItem::LTO(ref m) => format!("lto: {}", m.name()),
         }
     }
 }
 
-fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem)
-    -> Result<CompiledModule, FatalError>
+enum WorkItemResult {
+    Compiled(CompiledModule),
+    NeedsLTO(ModuleTranslation),
+}
+
+fn execute_work_item(cgcx: &CodegenContext,
+                     work_item: WorkItem,
+                     timeline: &mut Timeline)
+    -> Result<WorkItemResult, FatalError>
 {
     let diag_handler = cgcx.create_diag_handler();
-    let module_name = work_item.mtrans.name.clone();
-    let config = cgcx.config(work_item.mtrans.kind);
+    let config = cgcx.config(work_item.kind());
+    let mtrans = match work_item {
+        WorkItem::Optimize(mtrans) => mtrans,
+        WorkItem::LTO(mut lto) => {
+            unsafe {
+                let module = lto.optimize(cgcx, timeline)?;
+                let module = codegen(cgcx, &diag_handler, module, config, timeline)?;
+                return Ok(WorkItemResult::Compiled(module))
+            }
+        }
+    };
+    let module_name = mtrans.name.clone();
 
-    let pre_existing = match work_item.mtrans.source {
+    let pre_existing = match mtrans.source {
         ModuleSource::Translated(_) => None,
         ModuleSource::Preexisting(ref wp) => Some(wp.clone()),
     };
@@ -1029,13 +1224,33 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem)
         let incr_comp_session_dir = cgcx.incr_comp_session_dir
                                         .as_ref()
                                         .unwrap();
-        let name = &work_item.mtrans.name;
+        let name = &mtrans.name;
+        let mut object = None;
+        let mut bytecode = None;
+        let mut bytecode_compressed = None;
         for (kind, saved_file) in wp.saved_files {
-            let obj_out = cgcx.output_filenames.temp_path(kind, Some(name));
+            let obj_out = match kind {
+                WorkProductFileKind::Object => {
+                    let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(name));
+                    object = Some(path.clone());
+                    path
+                }
+                WorkProductFileKind::Bytecode => {
+                    let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name));
+                    bytecode = Some(path.clone());
+                    path
+                }
+                WorkProductFileKind::BytecodeCompressed => {
+                    let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name))
+                        .with_extension(RLIB_BYTECODE_EXTENSION);
+                    bytecode_compressed = Some(path.clone());
+                    path
+                }
+            };
             let source_file = in_incr_comp_dir(&incr_comp_session_dir,
                                                &saved_file);
             debug!("copying pre-existing module `{}` from {:?} to {}",
-                   work_item.mtrans.name,
+                   mtrans.name,
                    source_file,
                    obj_out.display());
             match link_or_copy(&source_file, &obj_out) {
@@ -1048,31 +1263,58 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem)
                 }
             }
         }
+        assert_eq!(object.is_some(), config.emit_obj);
+        assert_eq!(bytecode.is_some(), config.emit_bc);
+        assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed);
 
-        Ok(CompiledModule {
+        Ok(WorkItemResult::Compiled(CompiledModule {
+            llmod_id: mtrans.llmod_id.clone(),
             name: module_name,
             kind: ModuleKind::Regular,
             pre_existing: true,
-            symbol_name_hash: work_item.mtrans.symbol_name_hash,
-            emit_bc: config.emit_bc,
-            emit_obj: config.emit_obj,
-        })
+            object,
+            bytecode,
+            bytecode_compressed,
+        }))
     } else {
         debug!("llvm-optimizing {:?}", module_name);
 
         unsafe {
-            optimize_and_codegen(cgcx,
-                                 &diag_handler,
-                                 work_item.mtrans,
-                                 work_item.tm.0,
-                                 config)
+            optimize(cgcx, &diag_handler, &mtrans, config, timeline)?;
+
+            let lto = cgcx.lto;
+
+            let auto_thin_lto =
+                cgcx.thinlto &&
+                cgcx.total_cgus > 1 &&
+                mtrans.kind != ModuleKind::Allocator;
+
+            // 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) {
+                let module = codegen(cgcx, &diag_handler, mtrans, config, timeline)?;
+                Ok(WorkItemResult::Compiled(module))
+            } else {
+                Ok(WorkItemResult::NeedsLTO(mtrans))
+            }
         }
     }
 }
 
-#[derive(Debug)]
 enum Message {
     Token(io::Result<Acquired>),
+    NeedsLTO {
+        result: ModuleTranslation,
+        worker_id: usize,
+    },
     Done {
         result: Result<CompiledModule, ()>,
         worker_id: usize,
@@ -1087,7 +1329,7 @@ enum Message {
 
 struct Diagnostic {
     msg: String,
-    code: Option<String>,
+    code: Option<DiagnosticId>,
     lvl: Level,
 }
 
@@ -1103,12 +1345,13 @@ fn start_executing_work(tcx: TyCtxt,
                         shared_emitter: SharedEmitter,
                         trans_worker_send: Sender<Message>,
                         coordinator_receive: Receiver<Box<Any + Send>>,
+                        total_cgus: usize,
                         jobserver: Client,
                         time_graph: Option<TimeGraph>,
                         modules_config: Arc<ModuleConfig>,
                         metadata_config: Arc<ModuleConfig>,
                         allocator_config: Arc<ModuleConfig>)
-                        -> thread::JoinHandle<CompiledModules> {
+                        -> thread::JoinHandle<Result<CompiledModules, ()>> {
     let coordinator_send = tcx.tx_to_llvm_workers.clone();
     let mut exported_symbols = FxHashMap();
     exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE));
@@ -1135,17 +1378,36 @@ fn start_executing_work(tcx: TyCtxt,
 
     let mut each_linked_rlib_for_lto = Vec::new();
     drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| {
-        if link::ignored_for_lto(crate_info, cnum) {
+        if link::ignored_for_lto(sess, crate_info, cnum) {
             return
         }
         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,
-        lto: sess.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() },
+
         no_landing_pads: sess.no_landing_pads(),
+        save_temps: sess.opts.cg.save_temps,
         opts: Arc::new(sess.opts.clone()),
         time_passes: sess.time_passes(),
         exported_symbols,
@@ -1160,6 +1422,13 @@ fn start_executing_work(tcx: TyCtxt,
         regular_module_config: modules_config,
         metadata_module_config: metadata_config,
         allocator_module_config: allocator_config,
+        tm_factory: target_machine_factory(tcx.sess),
+        total_cgus,
+        msvc_imps_needed: msvc_imps_needed(tcx),
+        target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(),
+        binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen,
+        debuginfo: tcx.sess.opts.debuginfo,
+        wasm_import_memory: wasm_import_memory,
     };
 
     // This is the "main loop" of parallel work happening for parallel codegen.
@@ -1282,6 +1551,21 @@ fn start_executing_work(tcx: TyCtxt,
     // and whenever we're done with that work we release the semaphore. In this
     // manner we can ensure that the maximum number of parallel workers is
     // capped at any one point in time.
+    //
+    // LTO and the coordinator thread
+    // ------------------------------
+    //
+    // The final job the coordinator thread is responsible for is managing LTO
+    // and how that works. When LTO is requested what we'll to is collect all
+    // optimized LLVM modules into a local vector on the coordinator. Once all
+    // modules have been translated and optimized we hand this to the `lto`
+    // module for further optimization. The `lto` module will return back a list
+    // of more modules to work on, which the coordinator will continue to spawn
+    // work for.
+    //
+    // Each LLVM module is automatically sent back to the coordinator for LTO if
+    // necessary. There's already optimizations in place to avoid sending work
+    // back to the coordinator if LTO isn't requested.
     return thread::spawn(move || {
         // We pretend to be within the top-level LLVM time-passes task here:
         set_time_depth(1);
@@ -1304,6 +1588,8 @@ fn start_executing_work(tcx: TyCtxt,
         let mut compiled_modules = vec![];
         let mut compiled_metadata_module = None;
         let mut compiled_allocator_module = None;
+        let mut needs_lto = Vec::new();
+        let mut started_lto = false;
 
         // This flag tracks whether all items have gone through translations
         let mut translation_done = false;
@@ -1325,6 +1611,7 @@ fn start_executing_work(tcx: TyCtxt,
         while !translation_done ||
               work_items.len() > 0 ||
               running > 0 ||
+              needs_lto.len() > 0 ||
               main_thread_worker_state != MainThreadWorkerState::Idle {
 
             // While there are still CGUs to be translated, the coordinator has
@@ -1348,13 +1635,34 @@ fn start_executing_work(tcx: TyCtxt,
                             worker: get_worker_id(&mut free_worker_ids),
                             .. cgcx.clone()
                         };
-                        maybe_start_llvm_timer(cgcx.config(item.mtrans.kind),
+                        maybe_start_llvm_timer(cgcx.config(item.kind()),
                                                &mut llvm_start_time);
                         main_thread_worker_state = MainThreadWorkerState::LLVMing;
                         spawn_work(cgcx, item);
                     }
                 }
             } else {
+                // If we've finished everything related to normal translation
+                // then it must be the case that we've got some LTO work to do.
+                // Perform the serial work here of figuring out what we're
+                // going to LTO and then push a bunch of work items onto our
+                // queue to do LTO
+                if work_items.len() == 0 &&
+                   running == 0 &&
+                   main_thread_worker_state == MainThreadWorkerState::Idle {
+                    assert!(!started_lto);
+                    assert!(needs_lto.len() > 0);
+                    started_lto = true;
+                    let modules = mem::replace(&mut needs_lto, Vec::new());
+                    for (work, cost) in generate_lto_work(&cgcx, modules) {
+                        let insertion_index = work_items
+                            .binary_search_by_key(&cost, |&(_, cost)| cost)
+                            .unwrap_or_else(|e| e);
+                        work_items.insert(insertion_index, (work, cost));
+                        helper.request_token();
+                    }
+                }
+
                 // In this branch, we know that everything has been translated,
                 // so it's just a matter of determining whether the implicit
                 // Token is free to use for LLVM work.
@@ -1365,7 +1673,7 @@ fn start_executing_work(tcx: TyCtxt,
                                 worker: get_worker_id(&mut free_worker_ids),
                                 .. cgcx.clone()
                             };
-                            maybe_start_llvm_timer(cgcx.config(item.mtrans.kind),
+                            maybe_start_llvm_timer(cgcx.config(item.kind()),
                                                    &mut llvm_start_time);
                             main_thread_worker_state = MainThreadWorkerState::LLVMing;
                             spawn_work(cgcx, item);
@@ -1396,7 +1704,7 @@ fn start_executing_work(tcx: TyCtxt,
             while work_items.len() > 0 && running < tokens.len() {
                 let (item, _) = work_items.pop().unwrap();
 
-                maybe_start_llvm_timer(cgcx.config(item.mtrans.kind),
+                maybe_start_llvm_timer(cgcx.config(item.kind()),
                                        &mut llvm_start_time);
 
                 let cgcx = CodegenContext {
@@ -1499,10 +1807,21 @@ fn start_executing_work(tcx: TyCtxt,
                         }
                     }
                 }
+                Message::NeedsLTO { result, worker_id } => {
+                    assert!(!started_lto);
+                    if main_thread_worker_state == MainThreadWorkerState::LLVMing {
+                        main_thread_worker_state = MainThreadWorkerState::Idle;
+                    } else {
+                        running -= 1;
+                    }
+
+                    free_worker_ids.push(worker_id);
+                    needs_lto.push(result);
+                }
                 Message::Done { result: Err(()), worker_id: _ } => {
-                    shared_emitter.fatal("aborting due to worker thread panic");
+                    shared_emitter.fatal("aborting due to worker thread failure");
                     // Exit the coordinator thread
-                    panic!("aborting due to worker thread panic")
+                    return Err(())
                 }
                 Message::TranslateItem => {
                     bug!("the coordinator should not receive translation requests")
@@ -1520,14 +1839,19 @@ fn start_executing_work(tcx: TyCtxt,
                                     total_llvm_time);
         }
 
+        // Regardless of what order these modules completed in, report them to
+        // the backend in the same order every time to ensure that we're handing
+        // out deterministic results.
+        compiled_modules.sort_by(|a, b| a.name.cmp(&b.name));
+
         let compiled_metadata_module = compiled_metadata_module
             .expect("Metadata module not compiled?");
 
-        CompiledModules {
+        Ok(CompiledModules {
             modules: compiled_modules,
             metadata_module: compiled_metadata_module,
             allocator_module: compiled_allocator_module,
-        }
+        })
     });
 
     // A heuristic that determines if we have enough LLVM WorkItems in the
@@ -1570,20 +1894,22 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) {
         // we exit.
         struct Bomb {
             coordinator_send: Sender<Box<Any + Send>>,
-            result: Option<CompiledModule>,
+            result: Option<WorkItemResult>,
             worker_id: usize,
         }
         impl Drop for Bomb {
             fn drop(&mut self) {
-                let result = match self.result.take() {
-                    Some(compiled_module) => Ok(compiled_module),
-                    None => Err(())
+                let worker_id = self.worker_id;
+                let msg = match self.result.take() {
+                    Some(WorkItemResult::Compiled(m)) => {
+                        Message::Done { result: Ok(m), worker_id }
+                    }
+                    Some(WorkItemResult::NeedsLTO(m)) => {
+                        Message::NeedsLTO { result: m, worker_id }
+                    }
+                    None => Message::Done { result: Err(()), worker_id }
                 };
-
-                drop(self.coordinator_send.send(Box::new(Message::Done {
-                    result,
-                    worker_id: self.worker_id,
-                })));
+                drop(self.coordinator_send.send(Box::new(msg)));
             }
         }
 
@@ -1596,22 +1922,17 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) {
         // Execute the work itself, and if it finishes successfully then flag
         // ourselves as a success as well.
         //
-        // Note that we ignore the result coming out of `execute_work_item`
-        // which will tell us if the worker failed with a `FatalError`. If that
-        // has happened, however, then a diagnostic was sent off to the main
-        // thread, along with an `AbortIfErrors` message. In that case the main
-        // thread is already exiting anyway most likely.
-        //
-        // In any case, there's no need for us to take further action here, so
-        // we just ignore the result and then send off our message saying that
-        // we're done, which if `execute_work_item` failed is unlikely to be
-        // seen by the main thread, but hey we might as well try anyway.
+        // Note that we ignore any `FatalError` coming out of `execute_work_item`,
+        // as a diagnostic was already sent off to the main thread - just
+        // surface that there was an error in this worker.
         bomb.result = {
-            let _timing_guard = cgcx.time_graph
-                                .as_ref()
-                                .map(|tg| tg.start(time_graph::TimelineId(cgcx.worker),
-                                                   LLVM_WORK_PACKAGE_KIND));
-            Some(execute_work_item(&cgcx, work).unwrap())
+            let timeline = cgcx.time_graph.as_ref().map(|tg| {
+                tg.start(time_graph::TimelineId(cgcx.worker),
+                         LLVM_WORK_PACKAGE_KIND,
+                         &work.name())
+            });
+            let mut timeline = timeline.unwrap_or(Timeline::noop());
+            execute_work_item(&cgcx, work, &mut timeline).ok()
         };
     });
 }
@@ -1651,16 +1972,17 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
 
 pub unsafe fn with_llvm_pmb(llmod: ModuleRef,
                             config: &ModuleConfig,
+                            opt_level: llvm::CodeGenOptLevel,
                             f: &mut FnMut(llvm::PassManagerBuilderRef)) {
     // Create the PassManagerBuilder for LLVM. We configure it with
     // reasonable defaults and prepare it to actually populate the pass
     // manager.
     let builder = llvm::LLVMPassManagerBuilderCreate();
-    let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None);
     let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone);
     let inline_threshold = config.inline_threshold;
 
-    llvm::LLVMRustConfigurePassManagerBuilder(builder, opt_level,
+    llvm::LLVMRustConfigurePassManagerBuilder(builder,
+                                              opt_level,
                                               config.merge_functions,
                                               config.vectorize_slp,
                                               config.vectorize_loop);
@@ -1780,7 +2102,7 @@ impl SharedEmitterMain {
                         Some(ref code) => {
                             handler.emit_with_code(&MultiSpan::new(),
                                                    &diag.msg,
-                                                   &code,
+                                                   code.clone(),
                                                    diag.lvl);
                         }
                         None => {
@@ -1823,7 +2145,7 @@ pub struct OngoingCrateTranslation {
     coordinator_send: Sender<Box<Any + Send>>,
     trans_worker_receive: Receiver<Message>,
     shared_emitter_main: SharedEmitterMain,
-    future: thread::JoinHandle<CompiledModules>,
+    future: thread::JoinHandle<Result<CompiledModules, ()>>,
     output_filenames: Arc<OutputFilenames>,
 }
 
@@ -1831,7 +2153,11 @@ impl OngoingCrateTranslation {
     pub fn join(self, sess: &Session, dep_graph: &DepGraph) -> CrateTranslation {
         self.shared_emitter_main.check(sess, true);
         let compiled_modules = match self.future.join() {
-            Ok(compiled_modules) => compiled_modules,
+            Ok(Ok(compiled_modules)) => compiled_modules,
+            Ok(Err(())) => {
+                sess.abort_if_errors();
+                panic!("expected abort due to worker thread errors")
+            },
             Err(_) => {
                 sess.fatal("Error during translation/LLVM phase.");
             }
@@ -1845,15 +2171,14 @@ impl OngoingCrateTranslation {
 
         copy_module_artifacts_into_incr_comp_cache(sess,
                                                    dep_graph,
-                                                   &compiled_modules,
-                                                   &self.output_filenames);
+                                                   &compiled_modules);
         produce_final_output_artifacts(sess,
                                        &compiled_modules,
                                        &self.output_filenames);
 
         // FIXME: time_llvm_passes support - does this use a global context or
         // something?
-        if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() {
+        if sess.codegen_units() == 1 && sess.time_llvm_passes() {
             unsafe { llvm::LLVMRustPrintPassTimings(); }
         }
 
@@ -1867,6 +2192,7 @@ impl OngoingCrateTranslation {
 
             modules: compiled_modules.modules,
             allocator_module: compiled_modules.allocator_module,
+            metadata_module: compiled_modules.metadata_module,
         };
 
         if self.no_integrated_as {
@@ -1918,9 +2244,7 @@ impl OngoingCrateTranslation {
             Ok(Message::TranslateItem) => {
                 // Nothing to do
             }
-            Ok(message) => {
-                panic!("unexpected message: {:?}", message)
-            }
+            Ok(_) => panic!("unexpected message"),
             Err(_) => {
                 // One of the LLVM threads must have panicked, fall through so
                 // error handling can be reached.
@@ -1932,12 +2256,57 @@ impl OngoingCrateTranslation {
 pub fn submit_translated_module_to_llvm(tcx: TyCtxt,
                                         mtrans: ModuleTranslation,
                                         cost: u64) {
-    let llvm_work_item = WorkItem {
-        mtrans,
-        tm: TargetMachine(create_target_machine(tcx.sess)),
-    };
+    let llvm_work_item = WorkItem::Optimize(mtrans);
     drop(tcx.tx_to_llvm_workers.send(Box::new(Message::TranslationDone {
         llvm_work_item,
         cost,
     })));
 }
+
+fn msvc_imps_needed(tcx: TyCtxt) -> bool {
+    tcx.sess.target.target.options.is_like_msvc &&
+        tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib)
+}
+
+// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
+// This is required to satisfy `dllimport` references to static data in .rlibs
+// when using MSVC linker.  We do this only for data, as linker can fix up
+// code references on its own.
+// See #26591, #27438
+fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) {
+    if !cgcx.msvc_imps_needed {
+        return
+    }
+    // The x86 ABI seems to require that leading underscores are added to symbol
+    // names, so we need an extra underscore on 32-bit. There's also a leading
+    // '\x01' here which disables LLVM's symbol mangling (e.g. no extra
+    // underscores added in front).
+    let prefix = if cgcx.target_pointer_width == "32" {
+        "\x01__imp__"
+    } else {
+        "\x01__imp_"
+    };
+    unsafe {
+        let i8p_ty = Type::i8p_llcx(llcx);
+        let globals = base::iter_globals(llmod)
+            .filter(|&val| {
+                llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage &&
+                    llvm::LLVMIsDeclaration(val) == 0
+            })
+            .map(move |val| {
+                let name = CStr::from_ptr(llvm::LLVMGetValueName(val));
+                let mut imp_name = prefix.as_bytes().to_vec();
+                imp_name.extend(name.to_bytes());
+                let imp_name = CString::new(imp_name).unwrap();
+                (imp_name, val)
+            })
+            .collect::<Vec<_>>();
+        for (imp_name, val) in globals {
+            let imp = llvm::LLVMAddGlobal(llmod,
+                                          i8p_ty.to_ref(),
+                                          imp_name.as_ptr() as *const _);
+            llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty));
+            llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
+        }
+    }
+}