From e29aa1430bb45d18a5d3fcc5f3b7d20e99a57758 Mon Sep 17 00:00:00 2001 From: Stuart Pernsteiner Date: Mon, 11 Aug 2014 10:33:58 -0700 Subject: [PATCH] move back::link::write into a separate file --- src/librustc/back/link.rs | 478 +--------------------------------- src/librustc/back/lto.rs | 7 +- src/librustc/back/write.rs | 476 +++++++++++++++++++++++++++++++++ src/librustc/driver/config.rs | 16 +- src/librustc/driver/driver.rs | 35 +-- src/librustc/lib.rs | 1 + src/librustdoc/test.rs | 4 +- 7 files changed, 511 insertions(+), 506 deletions(-) create mode 100644 src/librustc/back/write.rs diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index cb71e598323..07fa63336e3 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -13,12 +13,11 @@ use super::archive; use super::rpath; use super::rpath::RPathConfig; use super::svh::Svh; +use super::write::{OutputTypeBitcode, OutputTypeExe, OutputTypeObject}; use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput}; use driver::config::NoDebugInfo; use driver::session::Session; use driver::config; -use llvm; -use llvm::ModuleRef; use metadata::common::LinkMeta; use metadata::{encoder, cstore, filesearch, csearch, loader, creader}; use middle::trans::context::CrateContext; @@ -28,13 +27,11 @@ use util::common::time; use util::ppaux; use util::sha2::{Digest, Sha256}; -use std::c_str::{ToCStr, CString}; use std::char; use std::collections::HashSet; use std::io::{fs, TempDir, Command}; use std::io; use std::mem; -use std::ptr; use std::str; use std::string::String; use flate; @@ -77,477 +74,6 @@ pub static RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: uint = RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; -#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] -pub enum OutputType { - OutputTypeBitcode, - OutputTypeAssembly, - OutputTypeLlvmAssembly, - OutputTypeObject, - OutputTypeExe, -} - -pub fn llvm_err(sess: &Session, msg: String) -> ! { - unsafe { - let cstr = llvm::LLVMRustGetLastError(); - if cstr == ptr::null() { - sess.fatal(msg.as_slice()); - } else { - let err = CString::new(cstr, true); - let err = String::from_utf8_lossy(err.as_bytes()); - sess.fatal(format!("{}: {}", - msg.as_slice(), - err.as_slice()).as_slice()); - } - } -} - -pub fn write_output_file( - sess: &Session, - target: llvm::TargetMachineRef, - pm: llvm::PassManagerRef, - m: ModuleRef, - output: &Path, - file_type: llvm::FileType) { - unsafe { - output.with_c_str(|output| { - let result = llvm::LLVMRustWriteOutputFile( - target, pm, m, output, file_type); - if !result { - llvm_err(sess, "could not write output".to_string()); - } - }) - } -} - -pub mod write { - - use super::super::lto; - use super::{write_output_file, OutputType}; - use super::{OutputTypeAssembly, OutputTypeBitcode}; - use super::{OutputTypeExe, OutputTypeLlvmAssembly}; - use super::{OutputTypeObject}; - use driver::driver::{CrateTranslation, OutputFilenames}; - use driver::config::NoDebugInfo; - use driver::session::Session; - use driver::config; - use llvm; - use llvm::{ModuleRef, TargetMachineRef, PassManagerRef}; - use util::common::time; - use syntax::abi; - - use std::c_str::ToCStr; - use std::io::{Command}; - use libc::{c_uint, c_int}; - use std::str; - - // On android, we by default compile for armv7 processors. This enables - // things like double word CAS instructions (rather than emulating them) - // which are *far* more efficient. This is obviously undesirable in some - // cases, so if any sort of target feature is specified we don't append v7 - // to the feature list. - // - // On iOS only armv7 and newer are supported. So it is useful to - // get all hardware potential via VFP3 (hardware floating point) - // and NEON (SIMD) instructions supported by LLVM. - // Note that without those flags various linking errors might - // arise as some of intrinsics are converted into function calls - // and nobody provides implementations those functions - fn target_feature<'a>(sess: &'a Session) -> &'a str { - match sess.targ_cfg.os { - abi::OsAndroid => { - if "" == sess.opts.cg.target_feature.as_slice() { - "+v7" - } else { - sess.opts.cg.target_feature.as_slice() - } - }, - abi::OsiOS if sess.targ_cfg.arch == abi::Arm => { - "+v7,+thumb2,+vfp3,+neon" - }, - _ => sess.opts.cg.target_feature.as_slice() - } - } - - pub fn run_passes(sess: &Session, - trans: &CrateTranslation, - output_types: &[OutputType], - output: &OutputFilenames) { - let llmod = trans.module; - let llcx = trans.context; - unsafe { - configure_llvm(sess); - - if sess.opts.cg.save_temps { - output.with_extension("no-opt.bc").with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - - let opt_level = match sess.opts.optimize { - config::No => llvm::CodeGenLevelNone, - config::Less => llvm::CodeGenLevelLess, - config::Default => llvm::CodeGenLevelDefault, - config::Aggressive => llvm::CodeGenLevelAggressive, - }; - let use_softfp = sess.opts.cg.soft_float; - - // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. - // FIXME: #11954: mac64 unwinding may not work with fp elim - let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || - (sess.targ_cfg.os == abi::OsMacos && - sess.targ_cfg.arch == abi::X86_64); - - // OSX has -dead_strip, which doesn't rely on ffunction_sections - // FIXME(#13846) this should be enabled for windows - let ffunction_sections = sess.targ_cfg.os != abi::OsMacos && - sess.targ_cfg.os != abi::OsWindows; - let fdata_sections = ffunction_sections; - - let reloc_model = match sess.opts.cg.relocation_model.as_slice() { - "pic" => llvm::RelocPIC, - "static" => llvm::RelocStatic, - "default" => llvm::RelocDefault, - "dynamic-no-pic" => llvm::RelocDynamicNoPic, - _ => { - sess.err(format!("{} is not a valid relocation mode", - sess.opts - .cg - .relocation_model).as_slice()); - sess.abort_if_errors(); - return; - } - }; - - let code_model = match sess.opts.cg.code_model.as_slice() { - "default" => llvm::CodeModelDefault, - "small" => llvm::CodeModelSmall, - "kernel" => llvm::CodeModelKernel, - "medium" => llvm::CodeModelMedium, - "large" => llvm::CodeModelLarge, - _ => { - sess.err(format!("{} is not a valid code model", - sess.opts - .cg - .code_model).as_slice()); - sess.abort_if_errors(); - return; - } - }; - - let tm = sess.targ_cfg - .target_strs - .target_triple - .as_slice() - .with_c_str(|t| { - sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| { - target_feature(sess).with_c_str(|features| { - llvm::LLVMRustCreateTargetMachine( - t, cpu, features, - code_model, - reloc_model, - opt_level, - true /* EnableSegstk */, - use_softfp, - no_fp_elim, - ffunction_sections, - fdata_sections, - ) - }) - }) - }); - - // Create the two optimizing pass managers. These mirror what clang - // does, and are by populated by LLVM's default PassManagerBuilder. - // Each manager has a different set of passes, but they also share - // some common passes. - let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); - let mpm = llvm::LLVMCreatePassManager(); - - // If we're verifying or linting, add them to the function pass - // manager. - let addpass = |pass: &str| { - pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) - }; - if !sess.no_verify() { assert!(addpass("verify")); } - - if !sess.opts.cg.no_prepopulate_passes { - llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); - llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - populate_llvm_passes(fpm, mpm, llmod, opt_level, - trans.no_builtins); - } - - for pass in sess.opts.cg.passes.iter() { - pass.as_slice().with_c_str(|s| { - if !llvm::LLVMRustAddPass(mpm, s) { - sess.warn(format!("unknown pass {}, ignoring", - *pass).as_slice()); - } - }) - } - - // Finally, run the actual optimization passes - time(sess.time_passes(), "llvm function passes", (), |()| - llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); - time(sess.time_passes(), "llvm module passes", (), |()| - llvm::LLVMRunPassManager(mpm, llmod)); - - // Deallocate managers that we're now done with - llvm::LLVMDisposePassManager(fpm); - llvm::LLVMDisposePassManager(mpm); - - // Emit the bytecode if we're either saving our temporaries or - // emitting an rlib. Whenever an rlib is created, the bytecode is - // inserted into the archive in order to allow LTO against it. - if sess.opts.cg.save_temps || - (sess.crate_types.borrow().contains(&config::CrateTypeRlib) && - sess.opts.output_types.contains(&OutputTypeExe)) { - output.temp_path(OutputTypeBitcode).with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - - if sess.lto() { - time(sess.time_passes(), "all lto passes", (), |()| - lto::run(sess, llmod, tm, trans.reachable.as_slice())); - - if sess.opts.cg.save_temps { - output.with_extension("lto.bc").with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - } - - // A codegen-specific pass manager is used to generate object - // files for an LLVM module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, - no_builtins: bool, f: |PassManagerRef|) { - unsafe { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm); - llvm::LLVMDisposePassManager(cpm); - } - } - - let mut object_file = None; - let mut needs_metadata = false; - for output_type in output_types.iter() { - let path = output.path(*output_type); - match *output_type { - OutputTypeBitcode => { - path.with_c_str(|buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf); - }) - } - OutputTypeLlvmAssembly => { - path.with_c_str(|output| { - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - llvm::LLVMRustPrintModule(cpm, llmod, output); - }) - }) - } - OutputTypeAssembly => { - // If we're not using the LLVM assembler, this function - // could be invoked specially with output_type_assembly, - // so in this case we still want the metadata object - // file. - let ty = OutputTypeAssembly; - let path = if sess.opts.output_types.contains(&ty) { - path - } else { - needs_metadata = true; - output.temp_path(OutputTypeAssembly) - }; - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - write_output_file(sess, tm, cpm, llmod, &path, - llvm::AssemblyFile); - }); - } - OutputTypeObject => { - object_file = Some(path); - } - OutputTypeExe => { - object_file = Some(output.temp_path(OutputTypeObject)); - needs_metadata = true; - } - } - } - - time(sess.time_passes(), "codegen passes", (), |()| { - match object_file { - Some(ref path) => { - with_codegen(tm, llmod, trans.no_builtins, |cpm| { - write_output_file(sess, tm, cpm, llmod, path, - llvm::ObjectFile); - }); - } - None => {} - } - if needs_metadata { - with_codegen(tm, trans.metadata_module, - trans.no_builtins, |cpm| { - let out = output.temp_path(OutputTypeObject) - .with_extension("metadata.o"); - write_output_file(sess, tm, cpm, - trans.metadata_module, &out, - llvm::ObjectFile); - }) - } - }); - - llvm::LLVMRustDisposeTargetMachine(tm); - llvm::LLVMDisposeModule(trans.metadata_module); - llvm::LLVMContextDispose(trans.metadata_context); - llvm::LLVMDisposeModule(llmod); - llvm::LLVMContextDispose(llcx); - if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } - } - } - - pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { - let pname = super::get_cc_prog(sess); - let mut cmd = Command::new(pname.as_slice()); - - cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject)) - .arg(outputs.temp_path(OutputTypeAssembly)); - debug!("{}", &cmd); - - match cmd.output() { - Ok(prog) => { - if !prog.status.success() { - sess.err(format!("linking with `{}` failed: {}", - pname, - prog.status).as_slice()); - sess.note(format!("{}", &cmd).as_slice()); - let mut note = prog.error.clone(); - note.push_all(prog.output.as_slice()); - sess.note(str::from_utf8(note.as_slice()).unwrap()); - sess.abort_if_errors(); - } - }, - Err(e) => { - sess.err(format!("could not exec the linker `{}`: {}", - pname, - e).as_slice()); - sess.abort_if_errors(); - } - } - } - - unsafe fn configure_llvm(sess: &Session) { - use std::sync::{Once, ONCE_INIT}; - static mut INIT: Once = ONCE_INIT; - - // Copy what clang does by turning on loop vectorization at O2 and - // slp vectorization at O3 - let vectorize_loop = !sess.opts.cg.no_vectorize_loops && - (sess.opts.optimize == config::Default || - sess.opts.optimize == config::Aggressive); - let vectorize_slp = !sess.opts.cg.no_vectorize_slp && - sess.opts.optimize == config::Aggressive; - - let mut llvm_c_strs = Vec::new(); - let mut llvm_args = Vec::new(); - { - let add = |arg: &str| { - let s = arg.to_c_str(); - llvm_args.push(s.as_ptr()); - llvm_c_strs.push(s); - }; - add("rustc"); // fake program name - if vectorize_loop { add("-vectorize-loops"); } - if vectorize_slp { add("-vectorize-slp"); } - if sess.time_llvm_passes() { add("-time-passes"); } - if sess.print_llvm_passes() { add("-debug-pass=Structure"); } - - for arg in sess.opts.cg.llvm_args.iter() { - add((*arg).as_slice()); - } - } - - INIT.doit(|| { - llvm::LLVMInitializePasses(); - - // Only initialize the platforms supported by Rust here, because - // using --llvm-root will have multiple platforms that rustllvm - // doesn't actually link to and it's pointless to put target info - // into the registry that Rust cannot generate machine code for. - llvm::LLVMInitializeX86TargetInfo(); - llvm::LLVMInitializeX86Target(); - llvm::LLVMInitializeX86TargetMC(); - llvm::LLVMInitializeX86AsmPrinter(); - llvm::LLVMInitializeX86AsmParser(); - - llvm::LLVMInitializeARMTargetInfo(); - llvm::LLVMInitializeARMTarget(); - llvm::LLVMInitializeARMTargetMC(); - llvm::LLVMInitializeARMAsmPrinter(); - llvm::LLVMInitializeARMAsmParser(); - - llvm::LLVMInitializeMipsTargetInfo(); - llvm::LLVMInitializeMipsTarget(); - llvm::LLVMInitializeMipsTargetMC(); - llvm::LLVMInitializeMipsAsmPrinter(); - llvm::LLVMInitializeMipsAsmParser(); - - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, - llvm_args.as_ptr()); - }); - } - - unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef, - mpm: llvm::PassManagerRef, - llmod: ModuleRef, - opt: llvm::CodeGenOptLevel, - no_builtins: bool) { - // Create the PassManagerBuilder for LLVM. We configure it with - // reasonable defaults and prepare it to actually populate the pass - // manager. - let builder = llvm::LLVMPassManagerBuilderCreate(); - match opt { - llvm::CodeGenLevelNone => { - // Don't add lifetime intrinsics at O0 - llvm::LLVMRustAddAlwaysInlinePass(builder, false); - } - llvm::CodeGenLevelLess => { - llvm::LLVMRustAddAlwaysInlinePass(builder, true); - } - // numeric values copied from clang - llvm::CodeGenLevelDefault => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, - 225); - } - llvm::CodeGenLevelAggressive => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, - 275); - } - } - llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint); - llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins); - - // Use the builder to populate the function/module pass managers. - llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm); - llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm); - llvm::LLVMPassManagerBuilderDispose(builder); - - match opt { - llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => { - "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s)); - } - _ => {} - }; - } -} - - /* * Name mangling and its relationship to metadata. This is complex. Read * carefully. @@ -878,7 +404,7 @@ pub fn get_ar_prog(sess: &Session) -> String { } } -fn remove(sess: &Session, path: &Path) { +pub fn remove(sess: &Session, path: &Path) { match fs::unlink(path) { Ok(..) => {} Err(e) => { diff --git a/src/librustc/back/lto.rs b/src/librustc/back/lto.rs index 6c6a07f3502..eeba3406490 100644 --- a/src/librustc/back/lto.rs +++ b/src/librustc/back/lto.rs @@ -9,6 +9,7 @@ // except according to those terms. use super::link; +use super::write; use driver::session; use driver::config; use llvm; @@ -119,9 +120,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, if !llvm::LLVMRustLinkInExternalBitcode(llmod, ptr as *const libc::c_char, bc_decoded.len() as libc::size_t) { - link::llvm_err(sess, - format!("failed to load bc of `{}`", - name.as_slice())); + write::llvm_err(sess, + format!("failed to load bc of `{}`", + name.as_slice())); } }); } diff --git a/src/librustc/back/write.rs b/src/librustc/back/write.rs new file mode 100644 index 00000000000..c2345390366 --- /dev/null +++ b/src/librustc/back/write.rs @@ -0,0 +1,476 @@ +// Copyright 2013-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::lto; +use back::link::get_cc_prog; +use driver::driver::{CrateTranslation, OutputFilenames}; +use driver::config::NoDebugInfo; +use driver::session::Session; +use driver::config; +use llvm; +use llvm::{ModuleRef, TargetMachineRef, PassManagerRef}; +use util::common::time; +use syntax::abi; + +use std::c_str::{ToCStr, CString}; +use std::io::Command; +use std::ptr; +use std::str; +use libc::{c_uint, c_int}; + + +#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] +pub enum OutputType { + OutputTypeBitcode, + OutputTypeAssembly, + OutputTypeLlvmAssembly, + OutputTypeObject, + OutputTypeExe, +} + + +pub fn llvm_err(sess: &Session, msg: String) -> ! { + unsafe { + let cstr = llvm::LLVMRustGetLastError(); + if cstr == ptr::null() { + sess.fatal(msg.as_slice()); + } else { + let err = CString::new(cstr, true); + let err = String::from_utf8_lossy(err.as_bytes()); + sess.fatal(format!("{}: {}", + msg.as_slice(), + err.as_slice()).as_slice()); + } + } +} + +pub fn write_output_file( + sess: &Session, + target: llvm::TargetMachineRef, + pm: llvm::PassManagerRef, + m: ModuleRef, + output: &Path, + file_type: llvm::FileType) { + unsafe { + output.with_c_str(|output| { + let result = llvm::LLVMRustWriteOutputFile( + target, pm, m, output, file_type); + if !result { + llvm_err(sess, "could not write output".to_string()); + } + }) + } +} + + +// On android, we by default compile for armv7 processors. This enables +// things like double word CAS instructions (rather than emulating them) +// which are *far* more efficient. This is obviously undesirable in some +// cases, so if any sort of target feature is specified we don't append v7 +// to the feature list. +// +// On iOS only armv7 and newer are supported. So it is useful to +// get all hardware potential via VFP3 (hardware floating point) +// and NEON (SIMD) instructions supported by LLVM. +// Note that without those flags various linking errors might +// arise as some of intrinsics are converted into function calls +// and nobody provides implementations those functions +fn target_feature<'a>(sess: &'a Session) -> &'a str { + match sess.targ_cfg.os { + abi::OsAndroid => { + if "" == sess.opts.cg.target_feature.as_slice() { + "+v7" + } else { + sess.opts.cg.target_feature.as_slice() + } + }, + abi::OsiOS if sess.targ_cfg.arch == abi::Arm => { + "+v7,+thumb2,+vfp3,+neon" + }, + _ => sess.opts.cg.target_feature.as_slice() + } +} + +pub fn run_passes(sess: &Session, + trans: &CrateTranslation, + output_types: &[OutputType], + output: &OutputFilenames) { + let llmod = trans.module; + let llcx = trans.context; + unsafe { + configure_llvm(sess); + + if sess.opts.cg.save_temps { + output.with_extension("no-opt.bc").with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + + let opt_level = match sess.opts.optimize { + config::No => llvm::CodeGenLevelNone, + config::Less => llvm::CodeGenLevelLess, + config::Default => llvm::CodeGenLevelDefault, + config::Aggressive => llvm::CodeGenLevelAggressive, + }; + let use_softfp = sess.opts.cg.soft_float; + + // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter. + // FIXME: #11954: mac64 unwinding may not work with fp elim + let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) || + (sess.targ_cfg.os == abi::OsMacos && + sess.targ_cfg.arch == abi::X86_64); + + // OSX has -dead_strip, which doesn't rely on ffunction_sections + // FIXME(#13846) this should be enabled for windows + let ffunction_sections = sess.targ_cfg.os != abi::OsMacos && + sess.targ_cfg.os != abi::OsWindows; + let fdata_sections = ffunction_sections; + + let reloc_model = match sess.opts.cg.relocation_model.as_slice() { + "pic" => llvm::RelocPIC, + "static" => llvm::RelocStatic, + "default" => llvm::RelocDefault, + "dynamic-no-pic" => llvm::RelocDynamicNoPic, + _ => { + sess.err(format!("{} is not a valid relocation mode", + sess.opts + .cg + .relocation_model).as_slice()); + sess.abort_if_errors(); + return; + } + }; + + let code_model = match sess.opts.cg.code_model.as_slice() { + "default" => llvm::CodeModelDefault, + "small" => llvm::CodeModelSmall, + "kernel" => llvm::CodeModelKernel, + "medium" => llvm::CodeModelMedium, + "large" => llvm::CodeModelLarge, + _ => { + sess.err(format!("{} is not a valid code model", + sess.opts + .cg + .code_model).as_slice()); + sess.abort_if_errors(); + return; + } + }; + + let tm = sess.targ_cfg + .target_strs + .target_triple + .as_slice() + .with_c_str(|t| { + sess.opts.cg.target_cpu.as_slice().with_c_str(|cpu| { + target_feature(sess).with_c_str(|features| { + llvm::LLVMRustCreateTargetMachine( + t, cpu, features, + code_model, + reloc_model, + opt_level, + true /* EnableSegstk */, + use_softfp, + no_fp_elim, + ffunction_sections, + fdata_sections, + ) + }) + }) + }); + + // Create the two optimizing pass managers. These mirror what clang + // does, and are by populated by LLVM's default PassManagerBuilder. + // Each manager has a different set of passes, but they also share + // some common passes. + let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); + let mpm = llvm::LLVMCreatePassManager(); + + // If we're verifying or linting, add them to the function pass + // manager. + let addpass = |pass: &str| { + pass.as_slice().with_c_str(|s| llvm::LLVMRustAddPass(fpm, s)) + }; + if !sess.no_verify() { assert!(addpass("verify")); } + + if !sess.opts.cg.no_prepopulate_passes { + llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); + llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); + populate_llvm_passes(fpm, mpm, llmod, opt_level, + trans.no_builtins); + } + + for pass in sess.opts.cg.passes.iter() { + pass.as_slice().with_c_str(|s| { + if !llvm::LLVMRustAddPass(mpm, s) { + sess.warn(format!("unknown pass {}, ignoring", + *pass).as_slice()); + } + }) + } + + // Finally, run the actual optimization passes + time(sess.time_passes(), "llvm function passes", (), |()| + llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); + time(sess.time_passes(), "llvm module passes", (), |()| + llvm::LLVMRunPassManager(mpm, llmod)); + + // Deallocate managers that we're now done with + llvm::LLVMDisposePassManager(fpm); + llvm::LLVMDisposePassManager(mpm); + + // Emit the bytecode if we're either saving our temporaries or + // emitting an rlib. Whenever an rlib is created, the bytecode is + // inserted into the archive in order to allow LTO against it. + if sess.opts.cg.save_temps || + (sess.crate_types.borrow().contains(&config::CrateTypeRlib) && + sess.opts.output_types.contains(&OutputTypeExe)) { + output.temp_path(OutputTypeBitcode).with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + + if sess.lto() { + time(sess.time_passes(), "all lto passes", (), |()| + lto::run(sess, llmod, tm, trans.reachable.as_slice())); + + if sess.opts.cg.save_temps { + output.with_extension("lto.bc").with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + } + + // A codegen-specific pass manager is used to generate object + // files for an LLVM module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef, + no_builtins: bool, f: |PassManagerRef|) { + unsafe { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm); + llvm::LLVMDisposePassManager(cpm); + } + } + + let mut object_file = None; + let mut needs_metadata = false; + for output_type in output_types.iter() { + let path = output.path(*output_type); + match *output_type { + OutputTypeBitcode => { + path.with_c_str(|buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf); + }) + } + OutputTypeLlvmAssembly => { + path.with_c_str(|output| { + with_codegen(tm, llmod, trans.no_builtins, |cpm| { + llvm::LLVMRustPrintModule(cpm, llmod, output); + }) + }) + } + OutputTypeAssembly => { + // If we're not using the LLVM assembler, this function + // could be invoked specially with output_type_assembly, + // so in this case we still want the metadata object + // file. + let ty = OutputTypeAssembly; + let path = if sess.opts.output_types.contains(&ty) { + path + } else { + needs_metadata = true; + output.temp_path(OutputTypeAssembly) + }; + with_codegen(tm, llmod, trans.no_builtins, |cpm| { + write_output_file(sess, tm, cpm, llmod, &path, + llvm::AssemblyFile); + }); + } + OutputTypeObject => { + object_file = Some(path); + } + OutputTypeExe => { + object_file = Some(output.temp_path(OutputTypeObject)); + needs_metadata = true; + } + } + } + + time(sess.time_passes(), "codegen passes", (), |()| { + match object_file { + Some(ref path) => { + with_codegen(tm, llmod, trans.no_builtins, |cpm| { + write_output_file(sess, tm, cpm, llmod, path, + llvm::ObjectFile); + }); + } + None => {} + } + if needs_metadata { + with_codegen(tm, trans.metadata_module, + trans.no_builtins, |cpm| { + let out = output.temp_path(OutputTypeObject) + .with_extension("metadata.o"); + write_output_file(sess, tm, cpm, + trans.metadata_module, &out, + llvm::ObjectFile); + }) + } + }); + + llvm::LLVMRustDisposeTargetMachine(tm); + llvm::LLVMDisposeModule(trans.metadata_module); + llvm::LLVMDisposeModule(llmod); + llvm::LLVMContextDispose(llcx); + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + } +} + +pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { + let pname = get_cc_prog(sess); + let mut cmd = Command::new(pname.as_slice()); + + cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject)) + .arg(outputs.temp_path(OutputTypeAssembly)); + debug!("{}", &cmd); + + match cmd.output() { + Ok(prog) => { + if !prog.status.success() { + sess.err(format!("linking with `{}` failed: {}", + pname, + prog.status).as_slice()); + sess.note(format!("{}", &cmd).as_slice()); + let mut note = prog.error.clone(); + note.push_all(prog.output.as_slice()); + sess.note(str::from_utf8(note.as_slice()).unwrap()); + sess.abort_if_errors(); + } + }, + Err(e) => { + sess.err(format!("could not exec the linker `{}`: {}", + pname, + e).as_slice()); + sess.abort_if_errors(); + } + } +} + +unsafe fn configure_llvm(sess: &Session) { + use std::sync::{Once, ONCE_INIT}; + static mut INIT: Once = ONCE_INIT; + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3 + let vectorize_loop = !sess.opts.cg.no_vectorize_loops && + (sess.opts.optimize == config::Default || + sess.opts.optimize == config::Aggressive); + let vectorize_slp = !sess.opts.cg.no_vectorize_slp && + sess.opts.optimize == config::Aggressive; + + let mut llvm_c_strs = Vec::new(); + let mut llvm_args = Vec::new(); + { + let add = |arg: &str| { + let s = arg.to_c_str(); + llvm_args.push(s.as_ptr()); + llvm_c_strs.push(s); + }; + add("rustc"); // fake program name + if vectorize_loop { add("-vectorize-loops"); } + if vectorize_slp { add("-vectorize-slp"); } + if sess.time_llvm_passes() { add("-time-passes"); } + if sess.print_llvm_passes() { add("-debug-pass=Structure"); } + + for arg in sess.opts.cg.llvm_args.iter() { + add((*arg).as_slice()); + } + } + + INIT.doit(|| { + llvm::LLVMInitializePasses(); + + // Only initialize the platforms supported by Rust here, because + // using --llvm-root will have multiple platforms that rustllvm + // doesn't actually link to and it's pointless to put target info + // into the registry that Rust cannot generate machine code for. + llvm::LLVMInitializeX86TargetInfo(); + llvm::LLVMInitializeX86Target(); + llvm::LLVMInitializeX86TargetMC(); + llvm::LLVMInitializeX86AsmPrinter(); + llvm::LLVMInitializeX86AsmParser(); + + llvm::LLVMInitializeARMTargetInfo(); + llvm::LLVMInitializeARMTarget(); + llvm::LLVMInitializeARMTargetMC(); + llvm::LLVMInitializeARMAsmPrinter(); + llvm::LLVMInitializeARMAsmParser(); + + llvm::LLVMInitializeMipsTargetInfo(); + llvm::LLVMInitializeMipsTarget(); + llvm::LLVMInitializeMipsTargetMC(); + llvm::LLVMInitializeMipsAsmPrinter(); + llvm::LLVMInitializeMipsAsmParser(); + + llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, + llvm_args.as_ptr()); + }); +} + +unsafe fn populate_llvm_passes(fpm: llvm::PassManagerRef, + mpm: llvm::PassManagerRef, + llmod: ModuleRef, + opt: llvm::CodeGenOptLevel, + no_builtins: bool) { + // Create the PassManagerBuilder for LLVM. We configure it with + // reasonable defaults and prepare it to actually populate the pass + // manager. + let builder = llvm::LLVMPassManagerBuilderCreate(); + match opt { + llvm::CodeGenLevelNone => { + // Don't add lifetime intrinsics at O0 + llvm::LLVMRustAddAlwaysInlinePass(builder, false); + } + llvm::CodeGenLevelLess => { + llvm::LLVMRustAddAlwaysInlinePass(builder, true); + } + // numeric values copied from clang + llvm::CodeGenLevelDefault => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, + 225); + } + llvm::CodeGenLevelAggressive => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, + 275); + } + } + llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint); + llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, no_builtins); + + // Use the builder to populate the function/module pass managers. + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm); + llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm); + llvm::LLVMPassManagerBuilderDispose(builder); + + match opt { + llvm::CodeGenLevelDefault | llvm::CodeGenLevelAggressive => { + "mergefunc".with_c_str(|s| llvm::LLVMRustAddPass(mpm, s)); + } + _ => {} + }; +} diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index c402c256a37..04315149a78 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -16,7 +16,7 @@ use driver::driver; use driver::session::Session; use back; -use back::link; +use back::write; use back::target_strs; use back::{arm, x86, x86_64, mips, mipsel}; use lint; @@ -72,7 +72,7 @@ pub struct Options { pub debuginfo: DebugInfoLevel, pub lint_opts: Vec<(String, lint::Level)>, pub describe_lints: bool, - pub output_types: Vec , + pub output_types: Vec , // This was mutable for rustpkg, which updates search paths based on the // parsed code. It remains mutable in case its replacements wants to use // this. @@ -646,11 +646,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { for unparsed_output_type in unparsed_output_types.iter() { for part in unparsed_output_type.as_slice().split(',') { let output_type = match part.as_slice() { - "asm" => link::OutputTypeAssembly, - "ir" => link::OutputTypeLlvmAssembly, - "bc" => link::OutputTypeBitcode, - "obj" => link::OutputTypeObject, - "link" => link::OutputTypeExe, + "asm" => write::OutputTypeAssembly, + "ir" => write::OutputTypeLlvmAssembly, + "bc" => write::OutputTypeBitcode, + "obj" => write::OutputTypeObject, + "link" => write::OutputTypeExe, _ => { early_error(format!("unknown emission type: `{}`", part).as_slice()) @@ -663,7 +663,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { output_types.as_mut_slice().sort(); output_types.dedup(); if output_types.len() == 0 { - output_types.push(link::OutputTypeExe); + output_types.push(write::OutputTypeExe); } let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m)); diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 5e9d8da5b4e..fa1a1b38a2d 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -10,6 +10,7 @@ use back::link; +use back::write; use driver::session::Session; use driver::config; use front; @@ -473,23 +474,23 @@ pub fn phase_5_run_llvm_passes(sess: &Session, trans: &CrateTranslation, outputs: &OutputFilenames) { if sess.opts.cg.no_integrated_as { - let output_type = link::OutputTypeAssembly; + let output_type = write::OutputTypeAssembly; time(sess.time_passes(), "LLVM passes", (), |_| - link::write::run_passes(sess, trans, [output_type], outputs)); + write::run_passes(sess, trans, [output_type], outputs)); - link::write::run_assembler(sess, outputs); + write::run_assembler(sess, outputs); // Remove assembly source, unless --save-temps was specified if !sess.opts.cg.save_temps { - fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap(); + fs::unlink(&outputs.temp_path(write::OutputTypeAssembly)).unwrap(); } } else { time(sess.time_passes(), "LLVM passes", (), |_| - link::write::run_passes(sess, - trans, - sess.opts.output_types.as_slice(), - outputs)); + write::run_passes(sess, + trans, + sess.opts.output_types.as_slice(), + outputs)); } } @@ -533,7 +534,7 @@ pub fn stop_after_phase_2(sess: &Session) -> bool { } pub fn stop_after_phase_5(sess: &Session) -> bool { - if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) { + if !sess.opts.output_types.iter().any(|&i| i == write::OutputTypeExe) { debug!("not building executable, returning early from compile_input"); return true; } @@ -549,7 +550,7 @@ fn write_out_deps(sess: &Session, for output_type in sess.opts.output_types.iter() { let file = outputs.path(*output_type); match *output_type { - link::OutputTypeExe => { + write::OutputTypeExe => { for output in sess.crate_types.borrow().iter() { let p = link::filename_for_input(sess, *output, id, &file); @@ -688,7 +689,7 @@ pub struct OutputFilenames { } impl OutputFilenames { - pub fn path(&self, flavor: link::OutputType) -> Path { + pub fn path(&self, flavor: write::OutputType) -> Path { match self.single_output_file { Some(ref path) => return path.clone(), None => {} @@ -696,14 +697,14 @@ impl OutputFilenames { self.temp_path(flavor) } - pub fn temp_path(&self, flavor: link::OutputType) -> Path { + pub fn temp_path(&self, flavor: write::OutputType) -> Path { let base = self.out_directory.join(self.filestem()); match flavor { - link::OutputTypeBitcode => base.with_extension("bc"), - link::OutputTypeAssembly => base.with_extension("s"), - link::OutputTypeLlvmAssembly => base.with_extension("ll"), - link::OutputTypeObject => base.with_extension("o"), - link::OutputTypeExe => base, + write::OutputTypeBitcode => base.with_extension("bc"), + write::OutputTypeAssembly => base.with_extension("s"), + write::OutputTypeLlvmAssembly => base.with_extension("ll"), + write::OutputTypeObject => base.with_extension("o"), + write::OutputTypeExe => base, } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 03dfcec18db..e4eac80c4af 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -69,6 +69,7 @@ pub mod back { pub mod link; pub mod lto; + pub mod write; } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 9df748e74e8..d2345614b25 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -20,7 +20,7 @@ use std::string::String; use std::collections::{HashSet, HashMap}; use testing; -use rustc::back::link; +use rustc::back::write; use rustc::driver::config; use rustc::driver::driver; use rustc::driver::session; @@ -120,7 +120,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet, externs: core::Exte maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), addl_lib_search_paths: RefCell::new(libs), crate_types: vec!(config::CrateTypeExecutable), - output_types: vec!(link::OutputTypeExe), + output_types: vec!(write::OutputTypeExe), no_trans: no_run, externs: externs, cg: config::CodegenOptions {