]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_trans/back/write.rs
Merge branch 'configure-lto' of https://github.com/alexcrichton/rust into rollup
[rust.git] / src / librustc_trans / back / write.rs
index a4af080ebf7e6f7e6d0effc2687e51aa5c9aef4c..db8db16a6c421d6dfd396ac77ddb0c51122f4a18 100644 (file)
@@ -11,6 +11,7 @@
 use back::bytecode::{self, RLIB_BYTECODE_EXTENSION};
 use back::lto::{self, ModuleBuffer, ThinBuffer};
 use back::link::{self, get_linker, remove};
+use back::command::Command;
 use back::linker::LinkerInfo;
 use back::symbol_export::ExportedSymbols;
 use base;
@@ -18,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,
+use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePasses,
                              AllPasses, Sanitizer, Lto};
 use rustc::session::Session;
 use rustc::util::nodemap::FxHashMap;
@@ -32,7 +33,7 @@
 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 rustc::util::fs::{link_or_copy};
 use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId};
 use errors::emitter::{Emitter};
 use syntax::attr;
@@ -68,8 +69,7 @@
     ("ropi-rwpi", llvm::RelocMode::ROPI_RWPI),
 ];
 
-pub const CODE_GEN_MODEL_ARGS : [(&'static str, llvm::CodeModel); 5] = [
-    ("default", llvm::CodeModel::Default),
+pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[
     ("small", llvm::CodeModel::Small),
     ("kernel", llvm::CodeModel::Kernel),
     ("medium", llvm::CodeModel::Medium),
@@ -170,20 +170,23 @@ pub fn target_machine_factory(sess: &Session)
     let ffunction_sections = sess.target.target.options.function_sections;
     let fdata_sections = ffunction_sections;
 
-    let code_model_arg = match sess.opts.cg.code_model {
-        Some(ref s) => &s,
-        None => &sess.target.target.options.code_model,
-    };
-
-    let code_model = match CODE_GEN_MODEL_ARGS.iter().find(
-        |&&arg| arg.0 == code_model_arg) {
-        Some(x) => x.1,
-        _ => {
-            sess.err(&format!("{:?} is not a valid code model",
-                              code_model_arg));
-            sess.abort_if_errors();
-            bug!();
+    let code_model_arg = sess.opts.cg.code_model.as_ref().or(
+        sess.target.target.options.code_model.as_ref(),
+    );
+
+    let code_model = match code_model_arg {
+        Some(s) => {
+            match CODE_GEN_MODEL_ARGS.iter().find(|arg| arg.0 == s) {
+                Some(x) => x.1,
+                _ => {
+                    sess.err(&format!("{:?} is not a valid code model",
+                                      code_model_arg));
+                    sess.abort_if_errors();
+                    bug!();
+                }
+            }
         }
+        None => llvm::CodeModel::None,
     };
 
     let singlethread = sess.target.target.options.singlethread;
@@ -258,6 +261,7 @@ pub struct ModuleConfig {
     // make the object file bitcode. Provides easy compatibility with
     // emscripten's ecc compiler, when used as the linker.
     obj_is_bitcode: bool,
+    no_integrated_as: bool,
 }
 
 impl ModuleConfig {
@@ -275,6 +279,7 @@ fn new(passes: Vec<String>) -> ModuleConfig {
             emit_asm: false,
             emit_obj: false,
             obj_is_bitcode: false,
+            no_integrated_as: false,
 
             no_verify: false,
             no_prepopulate_passes: false,
@@ -313,6 +318,12 @@ fn set_flags(&mut self, sess: &Session, no_builtins: bool) {
     }
 }
 
+/// Assembler name and command used by codegen when no_integrated_as is enabled
+struct AssemblerCommand {
+    name: PathBuf,
+    cmd: Command,
+}
+
 /// Additional resources used by optimize_and_codegen (not module specific)
 #[derive(Clone)]
 pub struct CodegenContext {
@@ -355,6 +366,8 @@ pub struct CodegenContext {
     // A reference to the TimeGraph so we can register timings. None means that
     // measuring is disabled.
     time_graph: Option<TimeGraph>,
+    // The assembler command if no_integrated_as option is enabled, None otherwise
+    assembler_cmd: Option<Arc<AssemblerCommand>>,
 }
 
 impl CodegenContext {
@@ -633,13 +646,17 @@ unsafe fn with_codegen<F, R>(tm: TargetMachineRef,
         !cgcx.crate_types.contains(&config::CrateTypeRlib) &&
         mtrans.kind == ModuleKind::Regular;
 
+    // If we don't have the integrated assembler, then we need to emit asm
+    // from LLVM and use `gcc` to create the object file.
+    let asm_to_obj = config.emit_obj && config.no_integrated_as;
+
     // 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 && !asm2wasm);
     let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm;
-    let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm;
+    let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm && !asm_to_obj;
     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);
@@ -719,13 +736,13 @@ extern "C" fn demangle_callback(input_ptr: *const c_char,
             timeline.record("ir");
         }
 
-        if config.emit_asm || (asm2wasm && config.emit_obj) {
+        if config.emit_asm || (asm2wasm && config.emit_obj) || asm_to_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
             // various errors like invalid IR or broken binaries, so we might have to clone the
             // module to produce the asm output
-            let llmod = if config.emit_obj {
+            let llmod = if config.emit_obj && !asm2wasm {
                 llvm::LLVMCloneModule(llmod)
             } else {
                 llmod
@@ -734,7 +751,7 @@ extern "C" fn demangle_callback(input_ptr: *const c_char,
                 write_output_file(diag_handler, tm, cpm, llmod, &path,
                                   llvm::FileType::AssemblyFile)
             })?;
-            if config.emit_obj {
+            if config.emit_obj && !asm2wasm {
                 llvm::LLVMDisposeModule(llmod);
             }
             timeline.record("asm");
@@ -754,6 +771,14 @@ extern "C" fn demangle_callback(input_ptr: *const c_char,
                                   llvm::FileType::ObjectFile)
             })?;
             timeline.record("obj");
+        } else if asm_to_obj {
+            let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
+            run_assembler(cgcx, diag_handler, &assembly, &obj_out);
+            timeline.record("asm_to_obj");
+
+            if !config.emit_asm && !cgcx.save_temps {
+                drop(fs::remove_file(&assembly));
+            }
         }
 
         Ok(())
@@ -835,7 +860,6 @@ pub fn start_async_translation(tcx: TyCtxt,
                                total_cgus: usize)
                                -> OngoingCrateTranslation {
     let sess = tcx.sess;
-    let crate_output = tcx.output_filenames(LOCAL_CRATE);
     let crate_name = tcx.crate_name(LOCAL_CRATE);
     let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins");
     let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs,
@@ -849,19 +873,9 @@ pub fn start_async_translation(tcx: TyCtxt,
         subsystem.to_string()
     });
 
-    let no_integrated_as = tcx.sess.opts.cg.no_integrated_as ||
-        (tcx.sess.target.target.options.no_integrated_as &&
-         (crate_output.outputs.contains_key(&OutputType::Object) ||
-          crate_output.outputs.contains_key(&OutputType::Exe)));
     let linker_info = LinkerInfo::new(tcx);
     let crate_info = CrateInfo::new(tcx);
 
-    let output_types_override = if no_integrated_as {
-        OutputTypes::new(&[(OutputType::Assembly, None)])
-    } else {
-        sess.opts.output_types.clone()
-    };
-
     // Figure out what we actually need to build.
     let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone());
     let mut metadata_config = ModuleConfig::new(vec![]);
@@ -907,7 +921,10 @@ pub fn start_async_translation(tcx: TyCtxt,
         allocator_config.emit_bc_compressed = true;
     }
 
-    for output_type in output_types_override.keys() {
+    modules_config.no_integrated_as = tcx.sess.opts.cg.no_integrated_as ||
+        tcx.sess.target.target.options.no_integrated_as;
+
+    for output_type in sess.opts.output_types.keys() {
         match *output_type {
             OutputType::Bitcode => { modules_config.emit_bc = true; }
             OutputType::LlvmAssembly => { modules_config.emit_ir = true; }
@@ -970,7 +987,6 @@ pub fn start_async_translation(tcx: TyCtxt,
         metadata,
         windows_subsystem,
         linker_info,
-        no_integrated_as,
         crate_info,
 
         time_graph,
@@ -1397,6 +1413,18 @@ fn start_executing_work(tcx: TyCtxt,
     let wasm_import_memory =
         attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory");
 
+    let assembler_cmd = if modules_config.no_integrated_as {
+        // HACK: currently we use linker (gcc) as our assembler
+        let (name, mut cmd, _) = get_linker(sess);
+        cmd.args(&sess.target.target.options.asm_args);
+        Some(Arc::new(AssemblerCommand {
+            name,
+            cmd,
+        }))
+    } else {
+        None
+    };
+
     let cgcx = CodegenContext {
         crate_types: sess.crate_types.borrow().clone(),
         each_linked_rlib_for_lto,
@@ -1425,6 +1453,7 @@ fn start_executing_work(tcx: TyCtxt,
         binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen,
         debuginfo: tcx.sess.opts.debuginfo,
         wasm_import_memory: wasm_import_memory,
+        assembler_cmd,
     };
 
     // This is the "main loop" of parallel work happening for parallel codegen.
@@ -1933,15 +1962,14 @@ fn drop(&mut self) {
     });
 }
 
-pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
-    let (pname, mut cmd, _) = get_linker(sess);
-
-    for arg in &sess.target.target.options.asm_args {
-        cmd.arg(arg);
-    }
+pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, object: &Path) {
+    let assembler = cgcx.assembler_cmd
+        .as_ref()
+        .expect("cgcx.assembler_cmd is missing?");
 
-    cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object))
-                           .arg(&outputs.temp_path(OutputType::Assembly, None));
+    let pname = &assembler.name;
+    let mut cmd = assembler.cmd.clone();
+    cmd.arg("-c").arg("-o").arg(object).arg(assembly);
     debug!("{:?}", cmd);
 
     match cmd.output() {
@@ -1950,18 +1978,18 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
                 let mut note = prog.stderr.clone();
                 note.extend_from_slice(&prog.stdout);
 
-                sess.struct_err(&format!("linking with `{}` failed: {}",
-                                         pname.display(),
-                                         prog.status))
+                handler.struct_err(&format!("linking with `{}` failed: {}",
+                                            pname.display(),
+                                            prog.status))
                     .note(&format!("{:?}", &cmd))
                     .note(str::from_utf8(&note[..]).unwrap())
                     .emit();
-                sess.abort_if_errors();
+                handler.abort_if_errors();
             }
         },
         Err(e) => {
-            sess.err(&format!("could not exec the linker `{}`: {}", pname.display(), e));
-            sess.abort_if_errors();
+            handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e));
+            handler.abort_if_errors();
         }
     }
 }
@@ -2135,7 +2163,6 @@ pub struct OngoingCrateTranslation {
     metadata: EncodedMetadata,
     windows_subsystem: Option<String>,
     linker_info: LinkerInfo,
-    no_integrated_as: bool,
     crate_info: CrateInfo,
     time_graph: Option<TimeGraph>,
     coordinator_send: Sender<Box<Any + Send>>,
@@ -2191,26 +2218,6 @@ pub(crate) fn join(self, sess: &Session, dep_graph: &DepGraph) -> CrateTranslati
             metadata_module: compiled_modules.metadata_module,
         };
 
-        if self.no_integrated_as {
-            run_assembler(sess,  &self.output_filenames);
-
-            // HACK the linker expects the object file to be named foo.0.o but
-            // `run_assembler` produces an object named just foo.o. Rename it if we
-            // are going to build an executable
-            if sess.opts.output_types.contains_key(&OutputType::Exe) {
-                let f =  self.output_filenames.path(OutputType::Object);
-                rename_or_copy_remove(&f,
-                    f.with_file_name(format!("{}.0.o",
-                                             f.file_stem().unwrap().to_string_lossy()))).unwrap();
-            }
-
-            // Remove assembly source, unless --save-temps was specified
-            if !sess.opts.cg.save_temps {
-                fs::remove_file(&self.output_filenames
-                                     .temp_path(OutputType::Assembly, None)).unwrap();
-            }
-        }
-
         trans
     }