rust/src/librustc_trans/back/write.rs

1084 lines
39 KiB
Rust
Raw Normal View History

// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use back::lto;
use back::link::{get_cc_prog, remove};
use session::config::{OutputFilenames, NoDebugInfo, Passes, SomePasses, AllPasses};
use session::Session;
use session::config;
use llvm;
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef};
use llvm::SMDiagnosticRef;
use trans::{CrateTranslation, ModuleTranslation};
use util::common::time;
use syntax::codemap;
use syntax::diagnostic;
use syntax::diagnostic::{Emitter, Handler, Level, mk_handler};
use std::c_str::{ToCStr, CString};
use std::io::Command;
use std::io::fs;
use std::iter::Unfold;
use std::ptr;
use std::str;
use std::mem;
use std::sync::{Arc, Mutex};
2014-12-06 18:34:37 -08:00
use std::thread;
use libc::{c_uint, c_int, c_void};
#[deriving(Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
pub enum OutputType {
OutputTypeBitcode,
OutputTypeAssembly,
OutputTypeLlvmAssembly,
OutputTypeObject,
OutputTypeExe,
}
pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! {
unsafe {
let cstr = llvm::LLVMRustGetLastError();
if cstr == ptr::null() {
2014-12-10 19:46:38 -08:00
handler.fatal(msg[]);
} else {
let err = CString::new(cstr, true);
let err = String::from_utf8_lossy(err.as_bytes());
handler.fatal(format!("{}: {}",
2014-12-10 19:46:38 -08:00
msg[],
err[])[]);
}
}
}
pub fn write_output_file(
handler: &diagnostic::Handler,
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(handler, "could not write output".to_string());
}
})
}
}
struct Diagnostic {
msg: String,
code: Option<String>,
lvl: Level,
}
// We use an Arc instead of just returning a list of diagnostics from the
// child task because we need to make sure that the messages are seen even
// if the child task panics (for example, when `fatal` is called).
#[deriving(Clone)]
struct SharedEmitter {
buffer: Arc<Mutex<Vec<Diagnostic>>>,
}
impl SharedEmitter {
fn new() -> SharedEmitter {
SharedEmitter {
buffer: Arc::new(Mutex::new(Vec::new())),
}
}
fn dump(&mut self, handler: &Handler) {
let mut buffer = self.buffer.lock();
for diag in buffer.iter() {
match diag.code {
Some(ref code) => {
handler.emit_with_code(None,
2014-12-10 19:46:38 -08:00
diag.msg[],
code[],
diag.lvl);
},
None => {
handler.emit(None,
2014-12-10 19:46:38 -08:00
diag.msg[],
diag.lvl);
},
}
}
buffer.clear();
}
}
impl Emitter for SharedEmitter {
fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>,
msg: &str, code: Option<&str>, lvl: Level) {
assert!(cmsp.is_none(), "SharedEmitter doesn't support spans");
self.buffer.lock().push(Diagnostic {
msg: msg.to_string(),
code: code.map(|s| s.to_string()),
lvl: lvl,
});
}
fn custom_emit(&mut self, _cm: &codemap::CodeMap,
_sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) {
panic!("SharedEmitter doesn't support custom_emit");
}
}
// 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(sess: &Session) -> String {
format!("{},{}", sess.target.target.options.features, sess.opts.cg.target_feature)
}
fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
match optimize {
config::No => llvm::CodeGenLevelNone,
config::Less => llvm::CodeGenLevelLess,
config::Default => llvm::CodeGenLevelDefault,
config::Aggressive => llvm::CodeGenLevelAggressive,
}
}
fn create_target_machine(sess: &Session) -> TargetMachineRef {
let reloc_model_arg = match sess.opts.cg.relocation_model {
2014-12-10 19:46:38 -08:00
Some(ref s) => s[],
None => sess.target.target.options.relocation_model[]
};
let reloc_model = match reloc_model_arg {
"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
2014-12-10 19:46:38 -08:00
.relocation_model)[]);
sess.abort_if_errors();
unreachable!();
}
};
let opt_level = get_llvm_opt_level(sess.opts.optimize);
let use_softfp = sess.opts.cg.soft_float;
// FIXME: #11906: Omitting frame pointers breaks retrieving the value of a parameter.
let no_fp_elim = (sess.opts.debuginfo != NoDebugInfo) ||
!sess.target.target.options.eliminate_frame_pointer;
let any_library = sess.crate_types.borrow().iter().any(|ty| {
*ty != config::CrateTypeExecutable
});
let ffunction_sections = sess.target.target.options.function_sections;
let fdata_sections = ffunction_sections;
let code_model_arg = match sess.opts.cg.code_model {
2014-12-10 19:46:38 -08:00
Some(ref s) => s[],
None => sess.target.target.options.code_model[]
};
let code_model = match code_model_arg {
"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
2014-12-10 19:46:38 -08:00
.code_model)[]);
sess.abort_if_errors();
unreachable!();
}
};
2014-12-10 19:46:38 -08:00
let triple = sess.target.target.llvm_target[];
let tm = unsafe {
triple.with_c_str(|t| {
let cpu = match sess.opts.cg.target_cpu {
2014-12-10 19:46:38 -08:00
Some(ref s) => s[],
None => sess.target.target.options.cpu[]
};
cpu.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,
!any_library && reloc_model == llvm::RelocPIC,
ffunction_sections,
fdata_sections,
)
})
})
})
};
if tm.is_null() {
llvm_err(sess.diagnostic().handler(),
format!("Could not create LLVM TargetMachine for triple: {}",
triple).to_string());
} else {
return tm;
};
}
/// Module-specific configuration for `optimize_and_codegen`.
#[deriving(Clone)]
struct ModuleConfig {
/// LLVM TargetMachine to use for codegen.
tm: TargetMachineRef,
/// 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>,
// Flags indicating which outputs to produce.
emit_no_opt_bc: bool,
emit_bc: bool,
emit_lto_bc: bool,
emit_ir: bool,
emit_asm: bool,
emit_obj: bool,
// Miscellaneous flags. These are mostly copied from command-line
// options.
no_verify: bool,
no_prepopulate_passes: bool,
no_builtins: bool,
time_passes: bool,
}
2014-12-06 11:39:25 -05:00
impl Send for ModuleConfig { }
impl ModuleConfig {
fn new(tm: TargetMachineRef, passes: Vec<String>) -> ModuleConfig {
ModuleConfig {
tm: tm,
passes: passes,
opt_level: None,
emit_no_opt_bc: false,
emit_bc: false,
emit_lto_bc: false,
emit_ir: false,
emit_asm: false,
emit_obj: false,
no_verify: false,
no_prepopulate_passes: false,
no_builtins: false,
time_passes: false,
}
}
fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) {
self.no_verify = sess.no_verify();
self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
self.no_builtins = trans.no_builtins;
self.time_passes = sess.time_passes();
}
}
/// Additional resources used by optimize_and_codegen (not module specific)
struct CodegenContext<'a> {
// Extra resources used for LTO: (sess, reachable). This will be `None`
// when running in a worker thread.
lto_ctxt: Option<(&'a Session, &'a [String])>,
// Handler to use for diagnostics produced during codegen.
handler: &'a Handler,
// LLVM optimizations for which we want to print remarks.
remark: Passes,
}
impl<'a> CodegenContext<'a> {
fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
CodegenContext {
lto_ctxt: Some((sess, reachable)),
handler: sess.diagnostic().handler(),
remark: sess.opts.cg.remark.clone(),
}
}
}
struct HandlerFreeVars<'a> {
llcx: ContextRef,
cgcx: &'a CodegenContext<'a>,
}
unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef,
user: *const c_void,
cookie: c_uint) {
use syntax::codemap::ExpnId;
let HandlerFreeVars { cgcx, .. }
= *mem::transmute::<_, *const HandlerFreeVars>(user);
let msg = llvm::build_string(|s| llvm::LLVMWriteSMDiagnosticToString(diag, s))
.expect("non-UTF8 SMDiagnostic");
match cgcx.lto_ctxt {
Some((sess, _)) => {
sess.codemap().with_expn_info(ExpnId::from_llvm_cookie(cookie), |info| match info {
2014-12-10 19:46:38 -08:00
Some(ei) => sess.span_err(ei.call_site, msg[]),
None => sess.err(msg[]),
});
}
None => {
2014-12-10 19:46:38 -08:00
cgcx.handler.err(msg[]);
cgcx.handler.note("build without -C codegen-units for more exact errors");
}
}
}
unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) {
let HandlerFreeVars { llcx, cgcx }
= *mem::transmute::<_, *const HandlerFreeVars>(user);
match llvm::diagnostic::Diagnostic::unpack(info) {
llvm::diagnostic::Optimization(opt) => {
let pass_name = CString::new(opt.pass_name, false);
let pass_name = pass_name.as_str().expect("got a non-UTF8 pass name from LLVM");
let enabled = match cgcx.remark {
AllPasses => true,
SomePasses(ref v) => v.iter().any(|s| *s == pass_name),
};
if enabled {
let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc);
cgcx.handler.note(format!("optimization {} for {} at {}: {}",
opt.kind.describe(),
pass_name,
2014-12-10 19:46:38 -08:00
if loc.is_empty() { "[unknown]" } else { loc[] },
llvm::twine_to_string(opt.message))[]);
}
}
_ => (),
}
}
// Unsafe due to LLVM calls.
unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
mtrans: ModuleTranslation,
config: ModuleConfig,
name_extra: String,
output_names: OutputFilenames) {
let ModuleTranslation { llmod, llcx } = mtrans;
let tm = config.tm;
// llcx doesn't outlive this function, so we can put this on the stack.
let fv = HandlerFreeVars {
llcx: llcx,
cgcx: cgcx,
};
let fv = &fv as *const HandlerFreeVars as *mut c_void;
llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv);
if !cgcx.remark.is_empty() {
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv);
}
if config.emit_no_opt_bc {
let ext = format!("{}.no-opt.bc", name_extra);
2014-12-10 19:46:38 -08:00
output_names.with_extension(ext[]).with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
}
match config.opt_level {
Some(opt_level) => {
// 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.with_c_str(|s| llvm::LLVMRustAddPass(fpm, s))
};
if !config.no_verify { assert!(addpass("verify")); }
if !config.no_prepopulate_passes {
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
populate_llvm_passes(fpm, mpm, llmod, opt_level,
config.no_builtins);
}
for pass in config.passes.iter() {
pass.with_c_str(|s| {
if !llvm::LLVMRustAddPass(mpm, s) {
cgcx.handler.warn(format!("unknown pass {}, ignoring",
2014-12-10 19:46:38 -08:00
*pass)[]);
}
})
}
// Finally, run the actual optimization passes
time(config.time_passes, "llvm function passes", (), |()|
llvm::LLVMRustRunFunctionPassManager(fpm, llmod));
time(config.time_passes, "llvm module passes", (), |()|
llvm::LLVMRunPassManager(mpm, llmod));
// Deallocate managers that we're now done with
llvm::LLVMDisposePassManager(fpm);
llvm::LLVMDisposePassManager(mpm);
match cgcx.lto_ctxt {
Some((sess, reachable)) if sess.lto() => {
time(sess.time_passes(), "all lto passes", (), |()|
lto::run(sess, llmod, tm, reachable));
if config.emit_lto_bc {
let name = format!("{}.lto.bc", name_extra);
2014-12-10 19:46:38 -08:00
output_names.with_extension(name[]).with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
}
},
_ => {},
}
},
None => {},
}
// 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.
2014-12-09 13:44:51 -05:00
unsafe fn with_codegen<F>(tm: TargetMachineRef,
llmod: ModuleRef,
no_builtins: bool,
f: F) where
F: FnOnce(PassManagerRef),
{
let cpm = llvm::LLVMCreatePassManager();
llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
f(cpm);
llvm::LLVMDisposePassManager(cpm);
}
if config.emit_bc {
let ext = format!("{}.bc", name_extra);
2014-12-10 19:46:38 -08:00
output_names.with_extension(ext[]).with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
}
time(config.time_passes, "codegen passes", (), |()| {
if config.emit_ir {
let ext = format!("{}.ll", name_extra);
2014-12-10 19:46:38 -08:00
output_names.with_extension(ext[]).with_c_str(|output| {
with_codegen(tm, llmod, config.no_builtins, |cpm| {
llvm::LLVMRustPrintModule(cpm, llmod, output);
})
})
}
if config.emit_asm {
2014-12-10 19:46:38 -08:00
let path = output_names.with_extension(format!("{}.s", name_extra)[]);
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFileType);
});
}
if config.emit_obj {
2014-12-10 19:46:38 -08:00
let path = output_names.with_extension(format!("{}.o", name_extra)[]);
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType);
});
}
});
llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
llvm::LLVMRustDisposeTargetMachine(tm);
}
pub fn run_passes(sess: &Session,
trans: &CrateTranslation,
output_types: &[config::OutputType],
crate_output: &OutputFilenames) {
// It's possible that we have `codegen_units > 1` but only one item in
// `trans.modules`. We could theoretically proceed and do LTO in that
// case, but it would be confusing to have the validity of
// `-Z lto -C codegen-units=2` depend on details of the crate being
// compiled, so we complain regardless.
if sess.lto() && sess.opts.cg.codegen_units > 1 {
// This case is impossible to handle because LTO expects to be able
// to combine the entire crate and all its dependencies into a
// single compilation unit, but each codegen unit is in a separate
// LLVM context, so they can't easily be combined.
sess.fatal("can't perform LTO when using multiple codegen units");
}
// Sanity check
assert!(trans.modules.len() == sess.opts.cg.codegen_units);
unsafe {
configure_llvm(sess);
}
let tm = create_target_machine(sess);
// Figure out what we actually need to build.
let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone());
let mut metadata_config = ModuleConfig::new(tm, vec!());
modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize));
// Save all versions of the bytecode if we're saving our temporaries.
if sess.opts.cg.save_temps {
modules_config.emit_no_opt_bc = true;
modules_config.emit_bc = true;
modules_config.emit_lto_bc = true;
metadata_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.
let needs_crate_bitcode =
sess.crate_types.borrow().contains(&config::CrateTypeRlib) &&
sess.opts.output_types.contains(&config::OutputTypeExe);
if needs_crate_bitcode {
modules_config.emit_bc = true;
}
for output_type in output_types.iter() {
match *output_type {
config::OutputTypeBitcode => { modules_config.emit_bc = true; },
config::OutputTypeLlvmAssembly => { modules_config.emit_ir = true; },
config::OutputTypeAssembly => {
modules_config.emit_asm = true;
// 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.
if !sess.opts.output_types.contains(&config::OutputTypeAssembly) {
metadata_config.emit_obj = true;
}
},
config::OutputTypeObject => { modules_config.emit_obj = true; },
config::OutputTypeExe => {
modules_config.emit_obj = true;
metadata_config.emit_obj = true;
},
rustc: Start "stabilizing" some flags This commit shuffles around some CLI flags of the compiler to some more stable locations with some renamings. The changes made were: * The `-v` flag has been repurposes as the "verbose" flag. The version flag has been renamed to `-V`. * The `-h` screen has been split into two parts. Most top-level options (not all) show with `-h`, and the remaining options (generally obscure) can be shown with `--help -v` which is a "verbose help screen" * The `-V` flag (version flag now) has lost its argument as it is now requested with `rustc -vV` "verbose version". * The `--emit` option has had its `ir` and `bc` variants renamed to `llvm-ir` and `llvm-bc` to emphasize that they are LLVM's IR/bytecode. * The `--emit` option has grown a new variant, `dep-info`, which subsumes the `--dep-info` CLI argument. The `--dep-info` flag is now deprecated. * The `--parse-only`, `--no-trans`, and `--no-analysis` flags have moved behind the `-Z` family of flags. * The `--debuginfo` and `--opt-level` flags were moved behind the top-level `-C` flag. * The `--print-file-name` and `--print-crate-name` flags were moved behind one global `--print` flag which now accepts one of `crate-name`, `file-names`, or `sysroot`. This global `--print` flag is intended to serve as a mechanism for learning various metadata about the compiler itself. No warnings are currently enabled to allow tools like Cargo to have time to migrate to the new flags before spraying warnings to all users.
2014-12-15 16:03:39 -08:00
config::OutputTypeDepInfo => {}
}
}
modules_config.set_flags(sess, trans);
metadata_config.set_flags(sess, trans);
// Populate a buffer with a list of codegen tasks. Items are processed in
// LIFO order, just because it's a tiny bit simpler that way. (The order
// doesn't actually matter.)
let mut work_items = Vec::with_capacity(1 + trans.modules.len());
{
let work = build_work_item(sess,
trans.metadata_module,
metadata_config.clone(),
crate_output.clone(),
"metadata".to_string());
work_items.push(work);
}
for (index, mtrans) in trans.modules.iter().enumerate() {
let work = build_work_item(sess,
*mtrans,
modules_config.clone(),
crate_output.clone(),
format!("{}", index));
work_items.push(work);
}
// Process the work items, optionally using worker threads.
if sess.opts.cg.codegen_units == 1 {
2014-12-10 19:46:38 -08:00
run_work_singlethreaded(sess, trans.reachable[], work_items);
} else {
run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units);
}
// All codegen is finished.
unsafe {
llvm::LLVMRustDisposeTargetMachine(tm);
}
// Produce final compile outputs.
let copy_if_one_unit = |ext: &str, output_type: config::OutputType, keep_numbered: bool| {
// Three cases:
if sess.opts.cg.codegen_units == 1 {
// 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
fs::copy(&crate_output.with_extension(ext),
&crate_output.path(output_type)).unwrap();
if !sess.opts.cg.save_temps && !keep_numbered {
// The user just wants `foo.x`, not `foo.0.x`.
remove(sess, &crate_output.with_extension(ext));
}
} else {
if crate_output.single_output_file.is_some() {
// 2) Multiple codegen units, with `-o some_name`. We have
// no good solution for this case, so warn the user.
sess.warn(format!("ignoring -o because multiple .{} files were produced",
2014-12-10 19:46:38 -08:00
ext)[]);
} else {
// 3) Multiple codegen units, but no `-o some_name`. We
// just leave the `foo.0.x` files in place.
// (We don't have to do any work in this case.)
}
}
};
let link_obj = |output_path: &Path| {
// Running `ld -r` on a single input is kind of pointless.
if sess.opts.cg.codegen_units == 1 {
fs::copy(&crate_output.with_extension("0.o"),
output_path).unwrap();
// Leave the .0.o file around, to mimic the behavior of the normal
// code path.
return;
}
// Some builds of MinGW GCC will pass --force-exe-suffix to ld, which
// will automatically add a .exe extension if the extension is not
// already .exe or .dll. To ensure consistent behavior on Windows, we
// add the .exe suffix explicitly and then rename the output file to
// the desired path. This will give the correct behavior whether or
// not GCC adds --force-exe-suffix.
let windows_output_path =
if sess.target.target.options.is_like_windows {
Some(output_path.with_extension("o.exe"))
} else {
None
};
let pname = get_cc_prog(sess);
2014-12-10 19:46:38 -08:00
let mut cmd = Command::new(pname[]);
2014-12-10 19:46:38 -08:00
cmd.args(sess.target.target.options.pre_link_args[]);
cmd.arg("-nostdlib");
for index in range(0, trans.modules.len()) {
2014-12-10 19:46:38 -08:00
cmd.arg(crate_output.with_extension(format!("{}.o", index)[]));
}
cmd.arg("-r")
.arg("-o")
.arg(windows_output_path.as_ref().unwrap_or(output_path));
2014-12-10 19:46:38 -08:00
cmd.args(sess.target.target.options.post_link_args[]);
if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 {
println!("{}", &cmd);
}
cmd.stdin(::std::io::process::Ignored)
.stdout(::std::io::process::InheritFd(1))
.stderr(::std::io::process::InheritFd(2));
match cmd.status() {
Ok(status) => {
if !status.success() {
sess.err(format!("linking of {} with `{}` failed",
2014-12-10 19:46:38 -08:00
output_path.display(), cmd)[]);
sess.abort_if_errors();
}
},
Err(e) => {
sess.err(format!("could not exec the linker `{}`: {}",
pname,
2014-12-10 19:46:38 -08:00
e)[]);
sess.abort_if_errors();
},
}
match windows_output_path {
Some(ref windows_path) => {
fs::rename(windows_path, output_path).unwrap();
},
None => {
// The file is already named according to `output_path`.
}
}
};
// Flag to indicate whether the user explicitly requested bitcode.
// Otherwise, we produced it only as a temporary output, and will need
// to get rid of it.
let mut user_wants_bitcode = false;
for output_type in output_types.iter() {
match *output_type {
config::OutputTypeBitcode => {
user_wants_bitcode = true;
// Copy to .bc, but always keep the .0.bc. There is a later
// check to figure out if we should delete .0.bc files, or keep
// them for making an rlib.
copy_if_one_unit("0.bc", config::OutputTypeBitcode, true);
}
config::OutputTypeLlvmAssembly => {
copy_if_one_unit("0.ll", config::OutputTypeLlvmAssembly, false);
}
config::OutputTypeAssembly => {
copy_if_one_unit("0.s", config::OutputTypeAssembly, false);
}
config::OutputTypeObject => {
link_obj(&crate_output.path(config::OutputTypeObject));
}
config::OutputTypeExe => {
// If config::OutputTypeObject is already in the list, then
// `crate.o` will be handled by the config::OutputTypeObject case.
// Otherwise, we need to create the temporary object so we
// can run the linker.
if !sess.opts.output_types.contains(&config::OutputTypeObject) {
link_obj(&crate_output.temp_path(config::OutputTypeObject));
}
}
rustc: Start "stabilizing" some flags This commit shuffles around some CLI flags of the compiler to some more stable locations with some renamings. The changes made were: * The `-v` flag has been repurposes as the "verbose" flag. The version flag has been renamed to `-V`. * The `-h` screen has been split into two parts. Most top-level options (not all) show with `-h`, and the remaining options (generally obscure) can be shown with `--help -v` which is a "verbose help screen" * The `-V` flag (version flag now) has lost its argument as it is now requested with `rustc -vV` "verbose version". * The `--emit` option has had its `ir` and `bc` variants renamed to `llvm-ir` and `llvm-bc` to emphasize that they are LLVM's IR/bytecode. * The `--emit` option has grown a new variant, `dep-info`, which subsumes the `--dep-info` CLI argument. The `--dep-info` flag is now deprecated. * The `--parse-only`, `--no-trans`, and `--no-analysis` flags have moved behind the `-Z` family of flags. * The `--debuginfo` and `--opt-level` flags were moved behind the top-level `-C` flag. * The `--print-file-name` and `--print-crate-name` flags were moved behind one global `--print` flag which now accepts one of `crate-name`, `file-names`, or `sysroot`. This global `--print` flag is intended to serve as a mechanism for learning various metadata about the compiler itself. No warnings are currently enabled to allow tools like Cargo to have time to migrate to the new flags before spraying warnings to all users.
2014-12-15 16:03:39 -08:00
config::OutputTypeDepInfo => {}
}
}
let user_wants_bitcode = user_wants_bitcode;
// Clean up unwanted temporary files.
// We create the following files by default:
// - crate.0.bc
// - crate.0.o
// - crate.metadata.bc
// - crate.metadata.o
// - crate.o (linked from crate.##.o)
// - crate.bc (copied from crate.0.bc)
// We may create additional files if requested by the user (through
// `-C save-temps` or `--emit=` flags).
if !sess.opts.cg.save_temps {
// Remove the temporary .0.o objects. If the user didn't
// explicitly request bitcode (with --emit=bc), and the bitcode is not
// needed for building an rlib, then we must remove .0.bc as well.
// Specific rules for keeping .0.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
// can toss .0.bc because we copied it to .bc earlier.
// - If we're not building an rlib and the user didn't request
// bitcode, then delete .0.bc.
// If you change how this works, also update back::link::link_rlib,
// where .0.bc files are (maybe) deleted after making an rlib.
let keep_numbered_bitcode = needs_crate_bitcode ||
(user_wants_bitcode && sess.opts.cg.codegen_units > 1);
for i in range(0, trans.modules.len()) {
if modules_config.emit_obj {
let ext = format!("{}.o", i);
2014-12-10 19:46:38 -08:00
remove(sess, &crate_output.with_extension(ext[]));
}
if modules_config.emit_bc && !keep_numbered_bitcode {
let ext = format!("{}.bc", i);
2014-12-10 19:46:38 -08:00
remove(sess, &crate_output.with_extension(ext[]));
}
}
if metadata_config.emit_bc && !user_wants_bitcode {
remove(sess, &crate_output.with_extension("metadata.bc"));
}
}
// We leave the following files around by default:
// - crate.o
// - crate.metadata.o
// - crate.bc
// These are used in linking steps and will be cleaned up afterward.
// FIXME: time_llvm_passes support - does this use a global context or
// something?
//if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
}
2014-11-26 10:07:58 -05:00
struct WorkItem {
mtrans: ModuleTranslation,
config: ModuleConfig,
output_names: OutputFilenames,
name_extra: String
}
fn build_work_item(sess: &Session,
mtrans: ModuleTranslation,
config: ModuleConfig,
output_names: OutputFilenames,
2014-11-26 10:07:58 -05:00
name_extra: String)
-> WorkItem
{
let mut config = config;
config.tm = create_target_machine(sess);
2014-11-26 10:07:58 -05:00
WorkItem { mtrans: mtrans, config: config, output_names: output_names,
name_extra: name_extra }
}
2014-11-26 10:07:58 -05:00
fn execute_work_item(cgcx: &CodegenContext,
work_item: WorkItem) {
unsafe {
optimize_and_codegen(cgcx, work_item.mtrans, work_item.config,
work_item.name_extra, work_item.output_names);
}
}
fn run_work_singlethreaded(sess: &Session,
reachable: &[String],
work_items: Vec<WorkItem>) {
let cgcx = CodegenContext::new_with_session(sess, reachable);
let mut work_items = work_items;
// Since we're running single-threaded, we can pass the session to
// the proc, allowing `optimize_and_codegen` to perform LTO.
for work in Unfold::new((), |_| work_items.pop()) {
2014-11-26 10:07:58 -05:00
execute_work_item(&cgcx, work);
}
}
fn run_work_multithreaded(sess: &Session,
work_items: Vec<WorkItem>,
num_workers: uint) {
// Run some workers to process the work items.
let work_items_arc = Arc::new(Mutex::new(work_items));
let mut diag_emitter = SharedEmitter::new();
let mut futures = Vec::with_capacity(num_workers);
for i in range(0, num_workers) {
let work_items_arc = work_items_arc.clone();
let diag_emitter = diag_emitter.clone();
let remark = sess.opts.cg.remark.clone();
2014-12-06 18:34:37 -08:00
let (tx, rx) = channel();
let mut tx = Some(tx);
futures.push(rx);
thread::Builder::new().name(format!("codegen-{}", i)).spawn(move |:| {
let diag_handler = mk_handler(box diag_emitter);
// Must construct cgcx inside the proc because it has non-Send
// fields.
let cgcx = CodegenContext {
lto_ctxt: None,
handler: &diag_handler,
remark: remark,
};
loop {
// Avoid holding the lock for the entire duration of the match.
let maybe_work = work_items_arc.lock().pop();
match maybe_work {
Some(work) => {
2014-11-26 10:07:58 -05:00
execute_work_item(&cgcx, work);
// Make sure to fail the worker so the main thread can
// tell that there were errors.
cgcx.handler.abort_if_errors();
}
None => break,
}
}
2014-12-06 18:34:37 -08:00
tx.take().unwrap().send(());
}).detach();
}
let mut panicked = false;
2014-12-06 18:34:37 -08:00
for rx in futures.into_iter() {
match rx.recv_opt() {
Ok(()) => {},
Err(_) => {
panicked = true;
},
}
// Display any new diagnostics.
diag_emitter.dump(sess.diagnostic().handler());
}
if panicked {
sess.fatal("aborting due to worker thread panic");
}
}
pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
let pname = get_cc_prog(sess);
2014-12-10 19:46:38 -08:00
let mut cmd = Command::new(pname[]);
cmd.arg("-c").arg("-o").arg(outputs.path(config::OutputTypeObject))
.arg(outputs.temp_path(config::OutputTypeAssembly));
debug!("{}", &cmd);
match cmd.output() {
Ok(prog) => {
if !prog.status.success() {
sess.err(format!("linking with `{}` failed: {}",
pname,
2014-12-10 19:46:38 -08:00
prog.status)[]);
sess.note(format!("{}", &cmd)[]);
let mut note = prog.error.clone();
2014-12-10 19:46:38 -08:00
note.push_all(prog.output[]);
sess.note(str::from_utf8(note[]).unwrap());
sess.abort_if_errors();
}
},
Err(e) => {
sess.err(format!("could not exec the linker `{}`: {}",
pname,
2014-12-10 19:46:38 -08:00
e)[]);
sess.abort_if_errors();
}
}
}
unsafe fn configure_llvm(sess: &Session) {
use std::sync::{Once, ONCE_INIT};
static 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() {
2014-12-10 19:46:38 -08:00
add((*arg)[]);
}
}
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));
}
_ => {}
};
}