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:
parent
e29aa1430b
commit
cf672850df
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -331,7 +331,7 @@ pub enum AsmDialect {
|
||||
AD_Intel = 1
|
||||
}
|
||||
|
||||
#[deriving(PartialEq)]
|
||||
#[deriving(PartialEq, Clone)]
|
||||
#[repr(C)]
|
||||
pub enum CodeGenOptLevel {
|
||||
CodeGenLevelNone = 0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user