Adapt backend to trans::partitioning dictating the codegen-unit setup.

This commit is contained in:
Michael Woerister 2016-05-13 20:48:32 -04:00
parent 2cd8cf92fc
commit 65e8a13441
8 changed files with 181 additions and 95 deletions

View File

@ -197,23 +197,70 @@ pub struct OutputFilenames {
pub outputs: HashMap<OutputType, Option<PathBuf>>,
}
/// Codegen unit names generated by the numbered naming scheme will contain this
/// marker right before the index of the codegen unit.
pub const NUMBERED_CODEGEN_UNIT_MARKER: &'static str = ".cgu-";
impl OutputFilenames {
pub fn path(&self, flavor: OutputType) -> PathBuf {
self.outputs.get(&flavor).and_then(|p| p.to_owned())
.or_else(|| self.single_output_file.clone())
.unwrap_or_else(|| self.temp_path(flavor))
.unwrap_or_else(|| self.temp_path(flavor, None))
}
pub fn temp_path(&self, flavor: OutputType) -> PathBuf {
/// Get the path where a compilation artifact of the given type for the
/// given codegen unit should be placed on disk. If codegen_unit_name is
/// None, a path distinct from those of any codegen unit will be generated.
pub fn temp_path(&self,
flavor: OutputType,
codegen_unit_name: Option<&str>)
-> PathBuf {
let extension = match flavor {
OutputType::Bitcode => "bc",
OutputType::Assembly => "s",
OutputType::LlvmAssembly => "ll",
OutputType::Object => "o",
OutputType::DepInfo => "d",
OutputType::Exe => "",
};
self.temp_path_ext(extension, codegen_unit_name)
}
/// Like temp_path, but also supports things where there is no corresponding
/// OutputType, like no-opt-bitcode or lto-bitcode.
pub fn temp_path_ext(&self,
ext: &str,
codegen_unit_name: Option<&str>)
-> PathBuf {
let base = self.out_directory.join(&self.filestem());
match flavor {
OutputType::Bitcode => base.with_extension("bc"),
OutputType::Assembly => base.with_extension("s"),
OutputType::LlvmAssembly => base.with_extension("ll"),
OutputType::Object => base.with_extension("o"),
OutputType::DepInfo => base.with_extension("d"),
OutputType::Exe => base,
let mut extension = String::new();
if let Some(codegen_unit_name) = codegen_unit_name {
if codegen_unit_name.contains(NUMBERED_CODEGEN_UNIT_MARKER) {
// If we use the numbered naming scheme for modules, we don't want
// the files to look like <crate-name><extra>.<crate-name>.<index>.<ext>
// but simply <crate-name><extra>.<index>.<ext>
let marker_offset = codegen_unit_name.rfind(NUMBERED_CODEGEN_UNIT_MARKER)
.unwrap();
let index_offset = marker_offset + NUMBERED_CODEGEN_UNIT_MARKER.len();
extension.push_str(&codegen_unit_name[index_offset .. ]);
} else {
extension.push_str(codegen_unit_name);
};
}
if !ext.is_empty() {
if !extension.is_empty() {
extension.push_str(".");
}
extension.push_str(ext);
}
let path = base.with_extension(&extension[..]);
path
}
pub fn with_extension(&self, extension: &str) -> PathBuf {

View File

@ -1081,7 +1081,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
// Remove assembly source, unless --save-temps was specified
if !sess.opts.cg.save_temps {
fs::remove_file(&outputs.temp_path(OutputType::Assembly)).unwrap();
fs::remove_file(&outputs.temp_path(OutputType::Assembly, None)).unwrap();
}
} else {
time(sess.time_passes(),

View File

@ -205,7 +205,7 @@ pub fn link_binary(sess: &Session,
// Remove the temporary object file and metadata if we aren't saving temps
if !sess.opts.cg.save_temps {
for obj in object_filenames(sess, outputs) {
for obj in object_filenames(trans, outputs) {
remove(sess, &obj);
}
remove(sess, &outputs.with_extension("metadata.o"));
@ -316,7 +316,7 @@ fn link_binary_output(sess: &Session,
crate_type: config::CrateType,
outputs: &OutputFilenames,
crate_name: &str) -> PathBuf {
let objects = object_filenames(sess, outputs);
let objects = object_filenames(trans, outputs);
let default_filename = filename_for_input(sess, crate_type, crate_name,
outputs);
let out_filename = outputs.outputs.get(&OutputType::Exe)
@ -356,10 +356,11 @@ fn link_binary_output(sess: &Session,
out_filename
}
fn object_filenames(sess: &Session, outputs: &OutputFilenames) -> Vec<PathBuf> {
(0..sess.opts.cg.codegen_units).map(|i| {
let ext = format!("{}.o", i);
outputs.temp_path(OutputType::Object).with_extension(&ext)
fn object_filenames(trans: &CrateTranslation,
outputs: &OutputFilenames)
-> Vec<PathBuf> {
trans.modules.iter().map(|module| {
outputs.temp_path(OutputType::Object, Some(&module.name[..]))
}).collect()
}
@ -497,7 +498,7 @@ fn link_rlib<'a>(sess: &'a Session,
ab.add_file(&bc_deflated_filename);
// See the bottom of back::write::run_passes for an explanation
// of when we do and don't keep .0.bc files around.
// of when we do and don't keep .#module-name#.bc files around.
let user_wants_numbered_bitcode =
sess.opts.output_types.contains_key(&OutputType::Bitcode) &&
sess.opts.cg.codegen_units > 1;

View File

@ -22,12 +22,12 @@ use libc;
use flate;
use std::ffi::CString;
use std::path::Path;
pub fn run(sess: &session::Session, llmod: ModuleRef,
tm: TargetMachineRef, reachable: &[String],
config: &ModuleConfig,
name_extra: &str,
output_names: &config::OutputFilenames) {
temp_no_opt_bc_filename: &Path) {
if sess.opts.cg.prefer_dynamic {
sess.struct_err("cannot prefer dynamic linking when performing LTO")
.note("only 'staticlib', 'bin', and 'cdylib' outputs are \
@ -132,8 +132,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
}
if sess.opts.cg.save_temps {
let path = output_names.with_extension(&format!("{}.no-opt.lto.bc", name_extra));
let cstr = path2cstr(&path);
let cstr = path2cstr(temp_no_opt_bc_filename);
unsafe {
llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
}

View File

@ -423,9 +423,9 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
mtrans: ModuleTranslation,
config: ModuleConfig,
name_extra: String,
output_names: OutputFilenames) {
let ModuleTranslation { llmod, llcx } = mtrans;
let llmod = mtrans.llmod;
let llcx = mtrans.llcx;
let tm = config.tm;
// llcx doesn't outlive this function, so we can put this on the stack.
@ -438,9 +438,10 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv);
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv);
let module_name = Some(&mtrans.name[..]);
if config.emit_no_opt_bc {
let ext = format!("{}.no-opt.bc", name_extra);
let out = output_names.with_extension(&ext);
let out = output_names.temp_path_ext("no-opt.bc", module_name);
let out = path2cstr(&out);
llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
}
@ -512,13 +513,18 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
match cgcx.lto_ctxt {
Some((sess, reachable)) if sess.lto() => {
time(sess.time_passes(), "all lto passes", ||
lto::run(sess, llmod, tm, reachable, &config,
&name_extra, &output_names));
time(sess.time_passes(), "all lto passes", || {
let temp_no_opt_bc_filename =
output_names.temp_path_ext("no-opt.lto.bc", module_name);
lto::run(sess,
llmod,
tm,
reachable,
&config,
&temp_no_opt_bc_filename);
});
if config.emit_lto_bc {
let name = format!("{}.lto.bc", name_extra);
let out = output_names.with_extension(&name);
let out = output_names.temp_path_ext("lto.bc", module_name);
let out = path2cstr(&out);
llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
}
@ -556,8 +562,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
let write_obj = config.emit_obj && !config.obj_is_bitcode;
let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode;
let bc_out = output_names.with_extension(&format!("{}.bc", name_extra));
let obj_out = output_names.with_extension(&format!("{}.o", name_extra));
let bc_out = output_names.temp_path(OutputType::Bitcode, module_name);
let obj_out = output_names.temp_path(OutputType::Object, module_name);
if write_bc {
let bc_out_c = path2cstr(&bc_out);
@ -566,8 +572,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
time(config.time_passes, &format!("codegen passes [{}]", cgcx.worker), || {
if config.emit_ir {
let ext = format!("{}.ll", name_extra);
let out = output_names.with_extension(&ext);
let out = output_names.temp_path(OutputType::LlvmAssembly, module_name);
let out = path2cstr(&out);
with_codegen(tm, llmod, config.no_builtins, |cpm| {
llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr());
@ -576,7 +581,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
}
if config.emit_asm {
let path = output_names.with_extension(&format!("{}.s", name_extra));
let path = output_names.temp_path(OutputType::Assembly, module_name);
// We can't use the same module for asm and binary output, because that triggers
// various errors like invalid IR or broken binaries, so we might have to clone the
@ -713,27 +718,29 @@ pub fn run_passes(sess: &Session,
{
let work = build_work_item(sess,
trans.metadata_module,
trans.metadata_module.clone(),
metadata_config.clone(),
crate_output.clone(),
"metadata".to_string());
crate_output.clone());
work_items.push(work);
}
for (index, mtrans) in trans.modules.iter().enumerate() {
for mtrans in trans.modules.iter() {
let work = build_work_item(sess,
*mtrans,
mtrans.clone(),
modules_config.clone(),
crate_output.clone(),
format!("{}", index));
crate_output.clone());
work_items.push(work);
}
// Process the work items, optionally using worker threads.
if sess.opts.cg.codegen_units == 1 {
// NOTE: This code is not really adapted to incremental compilation where
// the compiler decides the number of codegen units (and will
// potentially create hundreds of them).
let num_workers = work_items.len() - 1;
if num_workers == 1 {
run_work_singlethreaded(sess, &trans.reachable, work_items);
} else {
run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units);
run_work_multithreaded(sess, work_items, num_workers);
}
// All codegen is finished.
@ -748,32 +755,42 @@ pub fn run_passes(sess: &Session,
}
};
let copy_if_one_unit = |ext: &str,
output_type: OutputType,
let copy_if_one_unit = |output_type: OutputType,
keep_numbered: bool| {
if sess.opts.cg.codegen_units == 1 {
if trans.modules.len() == 1 {
// 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
copy_gracefully(&crate_output.with_extension(ext),
let module_name = Some(&(trans.modules[0].name)[..]);
let path = crate_output.temp_path(output_type, module_name);
copy_gracefully(&path,
&crate_output.path(output_type));
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));
// The user just wants `foo.x`, not `foo.#module-name#.x`.
remove(sess, &path);
}
} else if crate_output.outputs.contains_key(&output_type) {
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
// no good solution for this case, so warn the user.
sess.warn(&format!("ignoring emit path because multiple .{} files \
were produced", ext));
} else if crate_output.single_output_file.is_some() {
// 3) 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", ext));
} else {
// 4) Multiple codegen units, but no explicit name. We
// just leave the `foo.0.x` files in place.
// (We don't have to do any work in this case.)
let ext = crate_output.temp_path(output_type, None)
.extension()
.unwrap()
.to_str()
.unwrap()
.to_owned();
if crate_output.outputs.contains_key(&output_type) {
// 2) Multiple codegen units, with `--emit foo=some_name`. We have
// no good solution for this case, so warn the user.
sess.warn(&format!("ignoring emit path because multiple .{} files \
were produced", ext));
} else if crate_output.single_output_file.is_some() {
// 3) 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", ext));
} else {
// 4) Multiple codegen units, but no explicit name. We
// just leave the `foo.0.x` files in place.
// (We don't have to do any work in this case.)
}
}
};
@ -789,17 +806,19 @@ pub fn run_passes(sess: &Session,
// 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", OutputType::Bitcode, true);
copy_if_one_unit(OutputType::Bitcode, true);
}
OutputType::LlvmAssembly => {
copy_if_one_unit("0.ll", OutputType::LlvmAssembly, false);
copy_if_one_unit(OutputType::LlvmAssembly, false);
}
OutputType::Assembly => {
copy_if_one_unit("0.s", OutputType::Assembly, false);
// TODO: These are probably wrong
copy_if_one_unit(OutputType::Assembly, false);
}
OutputType::Object => {
user_wants_objects = true;
copy_if_one_unit("0.o", OutputType::Object, true);
// TODO: These are probably wrong
copy_if_one_unit(OutputType::Object, true);
}
OutputType::Exe |
OutputType::DepInfo => {}
@ -810,51 +829,55 @@ pub fn run_passes(sess: &Session,
// Clean up unwanted temporary files.
// We create the following files by default:
// - crate.0.bc
// - crate.0.o
// - crate.#module-name#.bc
// - crate.#module-name#.o
// - crate.metadata.bc
// - crate.metadata.o
// - crate.o (linked from crate.##.o)
// - crate.bc (copied from crate.0.bc)
// - crate.bc (copied from crate.##.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
// Remove the temporary .#module-name#.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.
// needed for building an rlib, then we must remove .#module-name#.bc as
// well.
// Specific rules for keeping .0.bc:
// Specific rules for keeping .#module-name#.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.
// can toss .#module-name#.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.
// bitcode, then delete .#module-name#.bc.
// If you change how this works, also update back::link::link_rlib,
// where .0.bc files are (maybe) deleted after making an rlib.
// where .#module-name#.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);
let keep_numbered_objects = needs_crate_object ||
(user_wants_objects && sess.opts.cg.codegen_units > 1);
for i in 0..trans.modules.len() {
for module_name in trans.modules.iter().map(|m| Some(&m.name[..])) {
if modules_config.emit_obj && !keep_numbered_objects {
let ext = format!("{}.o", i);
remove(sess, &crate_output.with_extension(&ext));
let path = crate_output.temp_path(OutputType::Object, module_name);
remove(sess, &path);
}
if modules_config.emit_bc && !keep_numbered_bitcode {
let ext = format!("{}.bc", i);
remove(sess, &crate_output.with_extension(&ext));
let path = crate_output.temp_path(OutputType::Bitcode, module_name);
remove(sess, &path);
}
}
if metadata_config.emit_bc && !user_wants_bitcode {
remove(sess, &crate_output.with_extension("metadata.bc"));
let path = crate_output.temp_path(OutputType::Bitcode,
Some(&trans.metadata_module.name[..]));
remove(sess, &path);
}
}
@ -874,28 +897,31 @@ pub fn run_passes(sess: &Session,
struct WorkItem {
mtrans: ModuleTranslation,
config: ModuleConfig,
output_names: OutputFilenames,
name_extra: String
output_names: OutputFilenames
}
fn build_work_item(sess: &Session,
mtrans: ModuleTranslation,
config: ModuleConfig,
output_names: OutputFilenames,
name_extra: String)
output_names: OutputFilenames)
-> WorkItem
{
let mut config = config;
config.tm = create_target_machine(sess);
WorkItem { mtrans: mtrans, config: config, output_names: output_names,
name_extra: name_extra }
WorkItem {
mtrans: mtrans,
config: config,
output_names: output_names
}
}
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);
optimize_and_codegen(cgcx,
work_item.mtrans,
work_item.config,
work_item.output_names);
}
}
@ -914,6 +940,8 @@ fn run_work_singlethreaded(sess: &Session,
fn run_work_multithreaded(sess: &Session,
work_items: Vec<WorkItem>,
num_workers: usize) {
assert!(num_workers > 0);
// Run some workers to process the work items.
let work_items_arc = Arc::new(Mutex::new(work_items));
let mut diag_emitter = SharedEmitter::new();
@ -981,7 +1009,7 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
let (pname, mut cmd, _) = get_linker(sess);
cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object))
.arg(&outputs.temp_path(OutputType::Assembly));
.arg(&outputs.temp_path(OutputType::Assembly, None));
debug!("{:?}", cmd);
match cmd.output() {

View File

@ -2630,6 +2630,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
});
let metadata_module = ModuleTranslation {
name: "metadata".to_string(),
llcx: shared_ccx.metadata_llcx(),
llmod: shared_ccx.metadata_llmod(),
};
@ -2644,7 +2645,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let crate_context_list = CrateContextList::new(&shared_ccx, codegen_units);
let modules = crate_context_list.iter()
.map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() })
.map(|ccx| ModuleTranslation {
name: String::from(&ccx.codegen_unit().name[..]),
llcx: ccx.llcx(),
llmod: ccx.llmod()
})
.collect();
// Skip crate items and just output metadata in -Z no-trans mode.
@ -2790,6 +2795,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
let linker_info = LinkerInfo::new(&shared_ccx, &reachable_symbols);
CrateTranslation {
modules: modules,
metadata_module: metadata_module,

View File

@ -129,8 +129,9 @@ mod type_;
mod type_of;
mod value;
#[derive(Copy, Clone)]
#[derive(Clone)]
pub struct ModuleTranslation {
pub name: String,
pub llcx: llvm::ContextRef,
pub llmod: llvm::ModuleRef,
}

View File

@ -121,6 +121,7 @@ use llvm;
use monomorphize;
use rustc::hir::def_id::DefId;
use rustc::hir::map::DefPathData;
use rustc::session::config::NUMBERED_CODEGEN_UNIT_MARKER;
use rustc::ty::TyCtxt;
use rustc::ty::item_path::characteristic_def_id_of_type;
use syntax::parse::token::{self, InternedString};
@ -283,7 +284,10 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning<
}
fn numbered_codegen_unit_name(crate_name: &str, index: usize) -> InternedString {
token::intern_and_get_ident(&format!("{}.{}", crate_name, index)[..])
token::intern_and_get_ident(&format!("{}{}{}",
crate_name,
NUMBERED_CODEGEN_UNIT_MARKER,
index)[..])
}
}