run optimization and codegen on worker threads

Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads.  (Previously, it used
`&Session` extensively, and `Session` is not `Share`.)  The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`.  The later linking steps can then be run unchanged.

The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit.  With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).

The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.)  Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file.  This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
This commit is contained in:
Stuart Pernsteiner 2014-07-17 10:52:52 -07:00
parent e29aa1430b
commit cf672850df
10 changed files with 770 additions and 282 deletions

View File

@ -1577,10 +1577,6 @@ fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) {
// codegen tests (vs. clang)
fn make_o_name(config: &Config, testfile: &Path) -> Path {
output_base_name(config, testfile).with_extension("o")
}
fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path {
if suffix.len() == 0 {
(*p).clone()
@ -1596,14 +1592,13 @@ fn compile_test_and_save_bitcode(config: &Config, props: &TestProps,
// FIXME (#9639): This needs to handle non-utf8 paths
let link_args = vec!("-L".to_string(),
aux_dir.as_str().unwrap().to_string());
let llvm_args = vec!("--emit=obj".to_string(),
"--crate-type=lib".to_string(),
"-C".to_string(),
"save-temps".to_string());
let llvm_args = vec!("--emit=bc,obj".to_string(),
"--crate-type=lib".to_string());
let args = make_compile_args(config,
props,
link_args.append(llvm_args.as_slice()),
|a, b| ThisFile(make_o_name(a, b)), testfile);
|a, b| ThisDirectory(output_base_name(a, b).dir_path()),
testfile);
compose_and_run_compiler(config, props, testfile, args, None)
}

View File

@ -662,51 +662,56 @@ fn link_rlib<'a>(sess: &'a Session,
ab.add_file(&metadata).unwrap();
remove(sess, &metadata);
// For LTO purposes, the bytecode of this library is also inserted
// into the archive.
//
// Note that we make sure that the bytecode filename in the archive
// is never exactly 16 bytes long by adding a 16 byte extension to
// it. This is to work around a bug in LLDB that would cause it to
// crash if the name of a file in an archive was exactly 16 bytes.
let bc_filename = obj_filename.with_extension("bc");
let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate");
if sess.opts.cg.codegen_units == 1 {
// For LTO purposes, the bytecode of this library is also
// inserted into the archive. We currently do this only when
// codegen_units == 1, so we don't have to deal with multiple
// bitcode files per crate.
//
// Note that we make sure that the bytecode filename in the
// archive is never exactly 16 bytes long by adding a 16 byte
// extension to it. This is to work around a bug in LLDB that
// would cause it to crash if the name of a file in an archive
// was exactly 16 bytes.
let bc_filename = obj_filename.with_extension("bc");
let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate");
let bc_data = match fs::File::open(&bc_filename).read_to_end() {
Ok(buffer) => buffer,
Err(e) => sess.fatal(format!("failed to read bytecode: {}",
e).as_slice())
};
let bc_data = match fs::File::open(&bc_filename).read_to_end() {
Ok(buffer) => buffer,
Err(e) => sess.fatal(format!("failed to read bytecode: {}",
e).as_slice())
};
let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) {
Some(compressed) => compressed,
None => sess.fatal(format!("failed to compress bytecode from {}",
bc_filename.display()).as_slice())
};
let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) {
Some(compressed) => compressed,
None => sess.fatal(format!("failed to compress bytecode from {}",
bc_filename.display()).as_slice())
};
let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) {
Ok(file) => file,
Err(e) => {
sess.fatal(format!("failed to create compressed bytecode \
file: {}", e).as_slice())
let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) {
Ok(file) => file,
Err(e) => {
sess.fatal(format!("failed to create compressed bytecode \
file: {}", e).as_slice())
}
};
match write_rlib_bytecode_object_v1(&mut bc_file_deflated,
bc_data_deflated.as_slice()) {
Ok(()) => {}
Err(e) => {
sess.err(format!("failed to write compressed bytecode: \
{}", e).as_slice());
sess.abort_if_errors()
}
};
ab.add_file(&bc_deflated_filename).unwrap();
remove(sess, &bc_deflated_filename);
if !sess.opts.cg.save_temps &&
!sess.opts.output_types.contains(&OutputTypeBitcode) {
remove(sess, &bc_filename);
}
};
match write_rlib_bytecode_object_v1(&mut bc_file_deflated,
bc_data_deflated.as_slice()) {
Ok(()) => {}
Err(e) => {
sess.err(format!("failed to write compressed bytecode: \
{}", e).as_slice());
sess.abort_if_errors()
}
};
ab.add_file(&bc_deflated_filename).unwrap();
remove(sess, &bc_deflated_filename);
if !sess.opts.cg.save_temps &&
!sess.opts.output_types.contains(&OutputTypeBitcode) {
remove(sess, &bc_filename);
}
}

View File

@ -67,7 +67,14 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
archive.read(format!("{}.bytecode.deflate",
file).as_slice())
});
let bc_encoded = bc_encoded.expect("missing compressed bytecode in archive!");
let bc_encoded = match bc_encoded {
Some(data) => data,
None => {
sess.fatal(format!("missing compressed bytecode in {} \
(perhaps it was compiled with -C codegen-units > 1)",
path.display()).as_slice());
},
};
let bc_extractor = if is_versioned_bytecode_format(bc_encoded) {
|_| {
// Read the version
@ -120,7 +127,7 @@ 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) {
write::llvm_err(sess,
write::llvm_err(sess.diagnostic().handler(),
format!("failed to load bc of `{}`",
name.as_slice()));
}

View File

@ -9,8 +9,8 @@
// except according to those terms.
use back::lto;
use back::link::get_cc_prog;
use driver::driver::{CrateTranslation, OutputFilenames};
use back::link::{get_cc_prog, remove};
use driver::driver::{CrateTranslation, ModuleTranslation, OutputFilenames};
use driver::config::NoDebugInfo;
use driver::session::Session;
use driver::config;
@ -18,11 +18,18 @@ use llvm;
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef};
use util::common::time;
use syntax::abi;
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::sync::{Arc, Mutex};
use std::task::TaskBuilder;
use libc::{c_uint, c_int};
@ -36,23 +43,23 @@ pub enum OutputType {
}
pub fn llvm_err(sess: &Session, msg: String) -> ! {
pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! {
unsafe {
let cstr = llvm::LLVMRustGetLastError();
if cstr == ptr::null() {
sess.fatal(msg.as_slice());
handler.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());
handler.fatal(format!("{}: {}",
msg.as_slice(),
err.as_slice()).as_slice());
}
}
}
pub fn write_output_file(
sess: &Session,
handler: &diagnostic::Handler,
target: llvm::TargetMachineRef,
pm: llvm::PassManagerRef,
m: ModuleRef,
@ -63,13 +70,74 @@ pub fn write_output_file(
let result = llvm::LLVMRustWriteOutputFile(
target, pm, m, output, file_type);
if !result {
llvm_err(sess, "could not write output".to_string());
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 fails (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,
diag.msg.as_slice(),
code.as_slice(),
diag.lvl);
},
None => {
handler.emit(None,
diag.msg.as_slice(),
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) {
fail!("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
@ -98,77 +166,68 @@ fn target_feature<'a>(sess: &'a Session) -> &'a str {
}
}
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);
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,
}
}
if sess.opts.cg.save_temps {
output.with_extension("no-opt.bc").with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
fn create_target_machine(sess: &Session) -> TargetMachineRef {
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();
unreachable!();
}
};
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;
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.
// 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);
// 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;
// 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();
unreachable!();
}
};
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| {
unsafe {
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(
@ -184,160 +243,531 @@ pub fn run_passes(sess: &Session,
)
})
})
});
})
}
}
// 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")); }
/// 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>,
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);
// 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,
}
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,
}
}
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());
}
})
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,
}
impl<'a> CodegenContext<'a> {
fn new(handler: &'a Handler) -> CodegenContext<'a> {
CodegenContext {
lto_ctxt: None,
handler: handler,
}
}
// 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);
})
fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
CodegenContext {
lto_ctxt: Some((sess, reachable)),
handler: sess.diagnostic().handler(),
}
}
}
if sess.lto() {
time(sess.time_passes(), "all lto passes", (), |()|
lto::run(sess, llmod, tm, trans.reachable.as_slice()));
// 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;
if sess.opts.cg.save_temps {
output.with_extension("lto.bc").with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
if config.emit_no_opt_bc {
let ext = format!("{}.no-opt.bc", name_extra);
output_names.with_extension(ext.as_slice()).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.as_slice().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.as_slice().with_c_str(|s| {
if !llvm::LLVMRustAddPass(mpm, s) {
cgcx.handler.warn(format!("unknown pass {}, ignoring",
*pass).as_slice());
}
})
}
}
// 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);
}
}
// 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));
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);
// 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);
output_names.with_extension(name.as_slice()).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.
unsafe fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef,
no_builtins: bool, f: |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);
output_names.with_extension(ext.as_slice()).with_c_str(|buf| {
llvm::LLVMWriteBitcodeToFile(llmod, buf);
})
}
time(config.time_passes, "codegen passes", (), |()| {
if config.emit_ir {
let ext = format!("{}.ll", name_extra);
output_names.with_extension(ext.as_slice()).with_c_str(|output| {
with_codegen(tm, llmod, config.no_builtins, |cpm| {
llvm::LLVMRustPrintModule(cpm, llmod, output);
})
})
}
if config.emit_asm {
let path = output_names.with_extension(format!("{}.s", name_extra).as_slice());
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::AssemblyFile);
});
}
if config.emit_obj {
let path = output_names.with_extension(format!("{}.o", name_extra).as_slice());
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFile);
});
}
});
llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
llvm::LLVMRustDisposeTargetMachine(tm);
}
pub fn run_passes(sess: &Session,
trans: &CrateTranslation,
output_types: &[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");
}
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 a bitcode file 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(&OutputTypeExe) &&
sess.opts.cg.codegen_units == 1;
if needs_crate_bitcode {
modules_config.emit_bc = true;
}
for output_type in output_types.iter() {
match *output_type {
OutputTypeBitcode => { modules_config.emit_bc = true; },
OutputTypeLlvmAssembly => { modules_config.emit_ir = true; },
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(&OutputTypeAssembly) {
metadata_config.emit_obj = true;
}
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;
},
OutputTypeObject => { modules_config.emit_obj = true; },
OutputTypeExe => {
modules_config.emit_obj = true;
metadata_config.emit_obj = true;
},
}
}
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 {
run_work_singlethreaded(sess, trans.reachable.as_slice(), work_items);
if needs_crate_bitcode {
// The only bitcode file produced (aside from metadata) was
// "crate.0.bc". Rename to "crate.bc" since that's what
// `link_rlib` expects to find.
fs::copy(&crate_output.with_extension("0.bc"),
&crate_output.temp_path(OutputTypeBitcode)).unwrap();
}
} else {
run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units);
assert!(!needs_crate_bitcode,
"can't produce a crate bitcode file from multiple compilation units");
}
// All codegen is finished.
unsafe {
llvm::LLVMRustDisposeTargetMachine(tm);
}
// Produce final compile outputs.
let copy_if_one_unit = |ext: &str, output_type: OutputType| {
// 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();
} 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 specified output filename \
because multiple .{} files were produced",
ext).as_slice());
} 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| {
let mut cmd = Command::new("ld");
for index in range(0, trans.modules.len()) {
cmd.arg(crate_output.with_extension(format!("{}.o", index).as_slice()));
}
cmd.arg("-r").arg("-o").arg(output_path);
cmd.stdin(::std::io::process::Ignored)
.stdout(::std::io::process::InheritFd(1))
.stderr(::std::io::process::InheritFd(2));
cmd.status().unwrap();
};
// 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.
// FIXME: Since we don't support LTO anyway, maybe we can avoid
// producing the temporary .0.bc's in the first place?
let mut save_bitcode = false;
for output_type in output_types.iter() {
match *output_type {
OutputTypeBitcode => {
save_bitcode = true;
copy_if_one_unit("0.bc", OutputTypeBitcode);
},
OutputTypeLlvmAssembly => { copy_if_one_unit("0.ll", OutputTypeLlvmAssembly); },
OutputTypeAssembly => { copy_if_one_unit("0.s", OutputTypeAssembly); },
OutputTypeObject => { link_obj(&crate_output.path(OutputTypeObject)); },
OutputTypeExe => {
// If OutputTypeObject is already in the list, then
// `crate.o` will be handled by the OutputTypeObject case.
// Otherwise, we need to create the temporary object so we
// can run the linker.
if !sess.opts.output_types.contains(&OutputTypeObject) {
link_obj(&crate_output.temp_path(OutputTypeObject));
}
},
}
}
let save_bitcode = save_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, or an empty bitcode file)
// 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), we must remove
// .0.bc as well. (We don't touch the crate.bc that may have been
// produced earlier.)
for i in range(0, trans.modules.len()) {
if modules_config.emit_obj {
let ext = format!("{}.o", i);
remove(sess, &crate_output.with_extension(ext.as_slice()));
}
if modules_config.emit_bc && !save_bitcode {
let ext = format!("{}.bc", i);
remove(sess, &crate_output.with_extension(ext.as_slice()));
}
}
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);
});
if metadata_config.emit_bc && !save_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(); }
}
type WorkItem = proc(&CodegenContext):Send;
fn build_work_item(sess: &Session,
mtrans: ModuleTranslation,
config: ModuleConfig,
output_names: OutputFilenames,
name_extra: String) -> WorkItem {
let mut config = config;
config.tm = create_target_machine(sess);
proc(cgcx) unsafe {
optimize_and_codegen(cgcx, mtrans, config, name_extra, 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()) {
work(&cgcx);
}
}
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 future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(proc() {
let diag_handler = mk_handler(box diag_emitter);
// Must construct cgcx inside the proc because it has non-Send
// fields.
let cgcx = CodegenContext::new(&diag_handler);
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) => {
work(&cgcx);
// Make sure to fail the worker so the main thread can
// tell that there were errors.
cgcx.handler.abort_if_errors();
}
None => break,
}
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);
})
}
});
futures.push(future);
}
llvm::LLVMRustDisposeTargetMachine(tm);
llvm::LLVMDisposeModule(trans.metadata_module);
llvm::LLVMDisposeModule(llmod);
llvm::LLVMContextDispose(llcx);
if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
let mut failed = false;
for future in futures.move_iter() {
match future.unwrap() {
Ok(()) => {},
Err(_) => {
failed = true;
},
}
// Display any new diagnostics.
diag_emitter.dump(sess.diagnostic().handler());
}
if failed {
sess.fatal("aborting due to worker thread failure");
}
}

View File

@ -303,6 +303,13 @@ macro_rules! cgoptions(
}
}
fn parse_uint(slot: &mut uint, v: Option<&str>) -> bool {
use std::from_str::FromStr;
match v.and_then(FromStr::from_str) {
Some(i) => { *slot = i; true },
None => false
}
}
}
) )
@ -347,6 +354,8 @@ cgoptions!(
"metadata to mangle symbol names with"),
extra_filename: String = ("".to_string(), parse_string,
"extra data to put in each output filename"),
codegen_units: uint = (1, parse_uint,
"divide crate into N units to optimize in parallel"),
)
pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions

View File

@ -442,11 +442,14 @@ pub fn phase_save_analysis(sess: &Session,
middle::save::process_crate(sess, krate, analysis, odir));
}
pub struct ModuleTranslation {
pub llcx: ContextRef,
pub llmod: ModuleRef,
}
pub struct CrateTranslation {
pub context: ContextRef,
pub module: ModuleRef,
pub metadata_context: ContextRef,
pub metadata_module: ModuleRef,
pub modules: Vec<ModuleTranslation>,
pub metadata_module: ModuleTranslation,
pub link: LinkMeta,
pub metadata: Vec<u8>,
pub reachable: Vec<String>,
@ -681,6 +684,7 @@ pub fn collect_crate_metadata(session: &Session,
session.opts.cg.metadata.clone()
}
#[deriving(Clone)]
pub struct OutputFilenames {
pub out_directory: Path,
pub out_filestem: String,

View File

@ -29,7 +29,7 @@ use back::link::{mangle_exported_name};
use back::{link, abi};
use driver::config;
use driver::config::{NoDebugInfo, FullDebugInfo};
use driver::driver::{CrateAnalysis, CrateTranslation};
use driver::driver::{CrateAnalysis, CrateTranslation, ModuleTranslation};
use driver::session::Session;
use lint;
use llvm::{BasicBlockRef, ModuleRef, ValueRef, Vector, get_param};
@ -47,8 +47,8 @@ use middle::trans::builder::{Builder, noname};
use middle::trans::callee;
use middle::trans::cleanup::{CleanupMethods, ScopeId};
use middle::trans::cleanup;
use middle::trans::common::{Block, C_bool, C_bytes, C_i32, C_integral, C_nil};
use middle::trans::common::{C_null, C_struct, C_u64, C_u8, C_uint, C_undef};
use middle::trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_integral, C_nil};
use middle::trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_uint, C_undef};
use middle::trans::common::{CrateContext, ExternMap, FunctionContext};
use middle::trans::common::{NodeInfo, Result, SubstP, monomorphize_type};
use middle::trans::common::{node_id_type, param_substs, return_type_is_void};
@ -2852,8 +2852,8 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
cx.sess().fatal("failed to compress metadata")
}
}.as_slice());
let llmeta = C_bytes(cx, compressed.as_slice());
let llconst = C_struct(cx, [llmeta], false);
let llmeta = C_bytes_in_context(cx.metadata_llcx(), compressed.as_slice());
let llconst = C_struct_in_context(cx.metadata_llcx(), [llmeta], false);
let name = format!("rust_metadata_{}_{}",
cx.link_meta().crate_name,
cx.link_meta().crate_hash);
@ -2896,8 +2896,7 @@ pub fn trans_crate(krate: ast::Crate,
let link_meta = link::build_link_meta(&tcx.sess, &krate, name);
// Multiple compilation units won't be supported until a later commit.
let codegen_units = 1;
let codegen_units = tcx.sess.opts.cg.codegen_units;
let shared_ccx = SharedCrateContext::new(link_meta.crate_name.as_slice(),
codegen_units,
tcx,
@ -2957,8 +2956,9 @@ pub fn trans_crate(krate: ast::Crate,
}
}
let llcx = shared_ccx.get_ccx(0).llcx();
let llmod = shared_ccx.get_ccx(0).llmod();
let modules = shared_ccx.iter()
.map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() })
.collect();
let mut reachable: Vec<String> = shared_ccx.reachable().iter().filter_map(|id| {
shared_ccx.item_symbols().borrow().find(id).map(|s| s.to_string())
@ -2988,20 +2988,22 @@ pub fn trans_crate(krate: ast::Crate,
// referenced from rt/rust_try.ll
reachable.push("rust_eh_personality_catch".to_string());
let metadata_module = shared_ccx.metadata_llmod();
let metadata_context = shared_ccx.metadata_llcx();
let metadata_module = ModuleTranslation {
llcx: shared_ccx.metadata_llcx(),
llmod: shared_ccx.metadata_llmod(),
};
let formats = shared_ccx.tcx().dependency_formats.borrow().clone();
let no_builtins = attr::contains_name(krate.attrs.as_slice(), "no_builtins");
(shared_ccx.take_tcx(), CrateTranslation {
context: llcx,
module: llmod,
link: link_meta,
let translation = CrateTranslation {
modules: modules,
metadata_module: metadata_module,
metadata_context: metadata_context,
link: link_meta,
metadata: metadata,
reachable: reachable,
crate_formats: formats,
no_builtins: no_builtins,
})
};
(shared_ccx.take_tcx(), translation)
}

View File

@ -14,7 +14,7 @@
use driver::session::Session;
use llvm;
use llvm::{ValueRef, BasicBlockRef, BuilderRef};
use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef};
use llvm::{True, False, Bool};
use middle::def;
use middle::freevars;
@ -669,9 +669,13 @@ pub fn C_binary_slice(cx: &CrateContext, data: &[u8]) -> ValueRef {
}
}
pub fn C_struct(ccx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef {
pub fn C_struct(cx: &CrateContext, elts: &[ValueRef], packed: bool) -> ValueRef {
C_struct_in_context(cx.llcx(), elts, packed)
}
pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef {
unsafe {
llvm::LLVMConstStructInContext(ccx.llcx(),
llvm::LLVMConstStructInContext(llcx,
elts.as_ptr(), elts.len() as c_uint,
packed as Bool)
}
@ -689,10 +693,14 @@ pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef {
}
}
pub fn C_bytes(ccx: &CrateContext, bytes: &[u8]) -> ValueRef {
pub fn C_bytes(cx: &CrateContext, bytes: &[u8]) -> ValueRef {
C_bytes_in_context(cx.llcx(), bytes)
}
pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef {
unsafe {
let ptr = bytes.as_ptr() as *const c_char;
return llvm::LLVMConstStringInContext(ccx.llcx(), ptr, bytes.len() as c_uint, True);
return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True);
}
}

View File

@ -148,6 +148,27 @@ pub struct CrateContext<'a> {
local: &'a LocalCrateContext,
}
pub struct CrateContextIterator<'a> {
shared: &'a SharedCrateContext,
index: uint,
}
impl<'a> Iterator<CrateContext<'a>> for CrateContextIterator<'a> {
fn next(&mut self) -> Option<CrateContext<'a>> {
if self.index >= self.shared.local_ccxs.len() {
return None;
}
let index = self.index;
self.index += 1;
Some(CrateContext {
shared: self.shared,
local: &self.shared.local_ccxs[index],
})
}
}
unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) {
let llcx = llvm::LLVMContextCreate();
let llmod = mod_name.with_c_str(|buf| {
@ -226,6 +247,13 @@ impl SharedCrateContext {
shared_ccx
}
pub fn iter<'a>(&'a self) -> CrateContextIterator<'a> {
CrateContextIterator {
shared: self,
index: 0,
}
}
pub fn get_ccx<'a>(&'a self, index: uint) -> CrateContext<'a> {
CrateContext {
shared: self,

View File

@ -331,7 +331,7 @@ pub enum AsmDialect {
AD_Intel = 1
}
#[deriving(PartialEq)]
#[deriving(PartialEq, Clone)]
#[repr(C)]
pub enum CodeGenOptLevel {
CodeGenLevelNone = 0,