1
Fork 0

async-llvm(8): Clean up resource management and drop LLVM modules ASAP.

This commit is contained in:
Michael Woerister 2017-07-25 17:26:24 +02:00
parent 4282dd87ea
commit 645841ea44
4 changed files with 256 additions and 151 deletions

View file

@ -237,8 +237,6 @@ pub fn compile_input(sess: &Session,
phase5_result);
phase5_result?;
write::cleanup_llvm(&trans);
phase_6_link_output(sess, &trans, &outputs);
// Now that we won't touch anything in the incremental compilation directory

View file

@ -18,7 +18,8 @@ use rustc::session::Session;
use llvm;
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef};
use llvm::SMDiagnosticRef;
use {CrateTranslation, OngoingCrateTranslation, ModuleLlvm, ModuleSource, ModuleTranslation};
use {CrateTranslation, OngoingCrateTranslation, ModuleSource, ModuleTranslation,
CompiledModule, ModuleKind};
use rustc::hir::def_id::CrateNum;
use rustc::util::common::{time, time_depth, set_time_depth, path2cstr};
use rustc::util::fs::link_or_copy;
@ -192,7 +193,6 @@ pub fn create_target_machine(sess: &Session) -> TargetMachineRef {
/// Module-specific configuration for `optimize_and_codegen`.
#[derive(Clone)]
pub struct ModuleConfig {
/// LLVM TargetMachine to use for codegen.
tm: TargetMachineRef,
@ -231,9 +231,9 @@ pub struct ModuleConfig {
unsafe impl Send for ModuleConfig { }
impl ModuleConfig {
fn new(tm: TargetMachineRef, passes: Vec<String>) -> ModuleConfig {
fn new(sess: &Session, passes: Vec<String>) -> ModuleConfig {
ModuleConfig {
tm: tm,
tm: create_target_machine(sess),
passes: passes,
opt_level: None,
opt_size: None,
@ -281,6 +281,40 @@ impl ModuleConfig {
self.merge_functions = sess.opts.optimize == config::OptLevel::Default ||
sess.opts.optimize == config::OptLevel::Aggressive;
}
fn clone(&self, sess: &Session) -> ModuleConfig {
ModuleConfig {
tm: create_target_machine(sess),
passes: self.passes.clone(),
opt_level: self.opt_level,
opt_size: self.opt_size,
emit_no_opt_bc: self.emit_no_opt_bc,
emit_bc: self.emit_bc,
emit_lto_bc: self.emit_lto_bc,
emit_ir: self.emit_ir,
emit_asm: self.emit_asm,
emit_obj: self.emit_obj,
obj_is_bitcode: self.obj_is_bitcode,
no_verify: self.no_verify,
no_prepopulate_passes: self.no_prepopulate_passes,
no_builtins: self.no_builtins,
time_passes: self.time_passes,
vectorize_loop: self.vectorize_loop,
vectorize_slp: self.vectorize_slp,
merge_functions: self.merge_functions,
inline_threshold: self.inline_threshold,
}
}
}
impl Drop for ModuleConfig {
fn drop(&mut self) {
unsafe {
llvm::LLVMRustDisposeTargetMachine(self.tm);
}
}
}
/// Additional resources used by optimize_and_codegen (not module specific)
@ -372,13 +406,17 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
diag_handler: &Handler,
mtrans: ModuleTranslation,
mllvm: ModuleLlvm,
config: ModuleConfig,
output_names: OutputFilenames)
-> Result<(), FatalError>
-> Result<CompiledModule, FatalError>
{
let llmod = mllvm.llmod;
let llcx = mllvm.llcx;
let (llmod, llcx) = match mtrans.source {
ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx),
ModuleSource::Preexisting(_) => {
bug!("optimize_and_codegen: called with ModuleSource::Preexisting")
}
};
let tm = config.tm;
let fv = HandlerFreeVars {
@ -390,7 +428,8 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv);
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv);
let module_name = Some(&mtrans.name[..]);
let module_name = mtrans.name.clone();
let module_name = Some(&module_name[..]);
if config.emit_no_opt_bc {
let out = output_names.temp_path_ext("no-opt.bc", module_name);
@ -606,30 +645,13 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
}
}
llvm::LLVMRustDisposeTargetMachine(tm);
Ok(())
Ok(mtrans.into_compiled_module(config.emit_obj, config.emit_bc))
}
pub fn cleanup_llvm(trans: &CrateTranslation) {
for module in trans.modules.iter() {
unsafe {
match module.source {
ModuleSource::Translated(llvm) => {
llvm::LLVMDisposeModule(llvm.llmod);
llvm::LLVMContextDispose(llvm.llcx);
}
ModuleSource::Preexisting(_) => {
}
}
}
}
}
pub struct RunLLVMPassesResult {
pub modules: Vec<ModuleTranslation>,
pub metadata_module: ModuleTranslation,
pub allocator_module: Option<ModuleTranslation>,
pub struct CompiledModules {
pub modules: Vec<CompiledModule>,
pub metadata_module: CompiledModule,
pub allocator_module: Option<CompiledModule>,
}
pub fn run_passes(sess: &Session,
@ -658,13 +680,11 @@ pub fn run_passes(sess: &Session,
!sess.opts.output_types.should_trans() ||
sess.opts.debugging_opts.no_trans);
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![]);
let mut allocator_config = ModuleConfig::new(tm, vec![]);
let mut modules_config = ModuleConfig::new(sess, sess.opts.cg.passes.clone());
let mut metadata_config = ModuleConfig::new(sess, vec![]);
let mut allocator_config = ModuleConfig::new(sess, vec![]);
if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer {
match *sanitizer {
@ -747,25 +767,22 @@ pub fn run_passes(sess: &Session,
let mut work_items = Vec::with_capacity(1 + modules.len());
{
let work = build_work_item(sess,
metadata_module.clone(),
metadata_config.clone(),
let work = build_work_item(metadata_module,
metadata_config.clone(sess),
crate_output.clone());
work_items.push(work);
}
if let Some(allocator) = allocator_module.clone() {
let work = build_work_item(sess,
allocator,
allocator_config.clone(),
if let Some(allocator) = allocator_module {
let work = build_work_item(allocator,
allocator_config.clone(sess),
crate_output.clone());
work_items.push(work);
}
for mtrans in modules.iter() {
let work = build_work_item(sess,
mtrans.clone(),
modules_config.clone(),
for mtrans in modules {
let work = build_work_item(mtrans,
modules_config.clone(sess),
crate_output.clone());
work_items.push(work);
}
@ -778,6 +795,10 @@ pub fn run_passes(sess: &Session,
Client::new(num_workers).expect("failed to create jobserver")
});
drop(modules_config);
drop(metadata_config);
drop(allocator_config);
let (shared_emitter, shared_emitter_main) = SharedEmitter::new();
let (trans_worker_send, trans_worker_receive) = channel();
let (coordinator_send, coordinator_receive) = channel();
@ -813,37 +834,27 @@ pub fn run_passes(sess: &Session,
}
}
match coordinator_thread.join() {
Ok(()) => {},
Err(err) => {
panic!("error: {:?}", err);
}
}
let compiled_modules = coordinator_thread.join().unwrap();
// Just in case, check this on the way out.
shared_emitter_main.check(sess);
sess.diagnostic().abort_if_errors();
// If in incr. comp. mode, preserve the `.o` files for potential re-use
for mtrans in modules.iter() {
for module in compiled_modules.modules.iter() {
let mut files = vec![];
if modules_config.emit_obj {
let path = crate_output.temp_path(OutputType::Object, Some(&mtrans.name));
if module.emit_obj {
let path = crate_output.temp_path(OutputType::Object, Some(&module.name));
files.push((OutputType::Object, path));
}
if modules_config.emit_bc {
let path = crate_output.temp_path(OutputType::Bitcode, Some(&mtrans.name));
if module.emit_bc {
let path = crate_output.temp_path(OutputType::Bitcode, Some(&module.name));
files.push((OutputType::Bitcode, path));
}
save_trans_partition(sess, &mtrans.name, mtrans.symbol_name_hash, &files);
}
// All codegen is finished.
unsafe {
llvm::LLVMRustDisposeTargetMachine(tm);
save_trans_partition(sess, &module.name, module.symbol_name_hash, &files);
}
let mut user_wants_bitcode = false;
@ -858,10 +869,10 @@ pub fn run_passes(sess: &Session,
let copy_if_one_unit = |output_type: OutputType,
keep_numbered: bool| {
if modules.len() == 1 {
if compiled_modules.modules.len() == 1 {
// 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
let module_name = Some(&modules[0].name[..]);
let module_name = Some(&compiled_modules.modules[0].name[..]);
let path = crate_output.temp_path(output_type, module_name);
copy_gracefully(&path,
&crate_output.path(output_type));
@ -962,27 +973,30 @@ pub fn run_passes(sess: &Session,
let keep_numbered_objects = needs_crate_object ||
(user_wants_objects && sess.opts.cg.codegen_units > 1);
for module_name in modules.iter().map(|m| Some(&m.name[..])) {
if modules_config.emit_obj && !keep_numbered_objects {
for module in compiled_modules.modules.iter() {
let module_name = Some(&module.name[..]);
if module.emit_obj && !keep_numbered_objects {
let path = crate_output.temp_path(OutputType::Object, module_name);
remove(sess, &path);
}
if modules_config.emit_bc && !keep_numbered_bitcode {
if module.emit_bc && !keep_numbered_bitcode {
let path = crate_output.temp_path(OutputType::Bitcode, module_name);
remove(sess, &path);
}
}
if metadata_config.emit_bc && !user_wants_bitcode {
if compiled_modules.metadata_module.emit_bc && !user_wants_bitcode {
let path = crate_output.temp_path(OutputType::Bitcode,
Some(&metadata_module.name));
Some(&compiled_modules.metadata_module.name));
remove(sess, &path);
}
if allocator_config.emit_bc && !user_wants_bitcode {
if let Some(ref module) = allocator_module {
if let Some(ref allocator_module) = compiled_modules.allocator_module {
if allocator_module.emit_bc && !user_wants_bitcode {
let path = crate_output.temp_path(OutputType::Bitcode,
Some(&module.name));
Some(&allocator_module.name));
remove(sess, &path);
}
}
@ -1000,21 +1014,14 @@ pub fn run_passes(sess: &Session,
unsafe { llvm::LLVMRustPrintPassTimings(); }
}
*trans.result.borrow_mut() = Some(
RunLLVMPassesResult {
modules,
metadata_module,
allocator_module,
}
);
*trans.result.borrow_mut() = Some(compiled_modules);
}
pub fn dump_incremental_data(trans: &CrateTranslation) {
let mut reuse = 0;
for mtrans in trans.modules.iter() {
match mtrans.source {
ModuleSource::Preexisting(..) => reuse += 1,
ModuleSource::Translated(..) => (),
if mtrans.pre_existing {
reuse += 1;
}
}
eprintln!("incremental: re-using {} out of {} modules", reuse, trans.modules.len());
@ -1032,14 +1039,11 @@ impl fmt::Debug for WorkItem {
}
}
fn build_work_item(sess: &Session,
mtrans: ModuleTranslation,
fn build_work_item(mtrans: ModuleTranslation,
config: ModuleConfig,
output_names: OutputFilenames)
-> WorkItem
{
let mut config = config;
config.tm = create_target_machine(sess);
WorkItem {
mtrans: mtrans,
config: config,
@ -1048,54 +1052,65 @@ fn build_work_item(sess: &Session,
}
fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem)
-> Result<(), FatalError>
-> Result<CompiledModule, FatalError>
{
let diag_handler = cgcx.create_diag_handler();
unsafe {
match work_item.mtrans.source {
ModuleSource::Translated(mllvm) => {
debug!("llvm-optimizing {:?}", work_item.mtrans.name);
optimize_and_codegen(cgcx,
&diag_handler,
work_item.mtrans,
mllvm,
work_item.config,
work_item.output_names)?;
}
ModuleSource::Preexisting(wp) => {
let incr_comp_session_dir = cgcx.incr_comp_session_dir
.as_ref()
.unwrap();
let name = &work_item.mtrans.name;
for (kind, saved_file) in wp.saved_files {
let obj_out = work_item.output_names.temp_path(kind, Some(name));
let source_file = in_incr_comp_dir(&incr_comp_session_dir,
&saved_file);
debug!("copying pre-existing module `{}` from {:?} to {}",
work_item.mtrans.name,
source_file,
obj_out.display());
match link_or_copy(&source_file, &obj_out) {
Ok(_) => { }
Err(err) => {
diag_handler.err(&format!("unable to copy {} to {}: {}",
source_file.display(),
obj_out.display(),
err));
}
}
let module_name = work_item.mtrans.name.clone();
let pre_existing = match work_item.mtrans.source {
ModuleSource::Translated(_) => None,
ModuleSource::Preexisting(ref wp) => Some(wp.clone()),
};
if let Some(wp) = pre_existing {
let incr_comp_session_dir = cgcx.incr_comp_session_dir
.as_ref()
.unwrap();
let name = &work_item.mtrans.name;
for (kind, saved_file) in wp.saved_files {
let obj_out = work_item.output_names.temp_path(kind, Some(name));
let source_file = in_incr_comp_dir(&incr_comp_session_dir,
&saved_file);
debug!("copying pre-existing module `{}` from {:?} to {}",
work_item.mtrans.name,
source_file,
obj_out.display());
match link_or_copy(&source_file, &obj_out) {
Ok(_) => { }
Err(err) => {
diag_handler.err(&format!("unable to copy {} to {}: {}",
source_file.display(),
obj_out.display(),
err));
}
}
}
}
Ok(())
Ok(CompiledModule {
name: module_name,
kind: ModuleKind::Regular,
pre_existing: true,
symbol_name_hash: work_item.mtrans.symbol_name_hash,
emit_bc: work_item.config.emit_bc,
emit_obj: work_item.config.emit_obj,
})
} else {
debug!("llvm-optimizing {:?}", module_name);
unsafe {
optimize_and_codegen(cgcx,
&diag_handler,
work_item.mtrans,
work_item.config,
work_item.output_names)
}
}
}
#[derive(Debug)]
pub enum Message {
Token(io::Result<Acquired>),
Done { success: bool },
Done { result: Result<CompiledModule, ()> },
WorkItem(WorkItem),
CheckErrorMessages,
}
@ -1115,7 +1130,7 @@ fn start_executing_work(sess: &Session,
coordinator_receive: Receiver<Message>,
jobserver: Client,
exported_symbols: Arc<ExportedSymbols>)
-> thread::JoinHandle<()> {
-> thread::JoinHandle<CompiledModules> {
// First up, convert our jobserver into a helper thread so we can use normal
// mpsc channels to manage our messages and such. Once we've got the helper
// thread then request `n-1` tokens because all of our work items are ready
@ -1215,6 +1230,10 @@ fn start_executing_work(sess: &Session,
// the jobserver.
thread::spawn(move || {
let mut compiled_modules = vec![];
let mut compiled_metadata_module = None;
let mut compiled_allocator_module = None;
let mut work_items_left = total_work_item_count;
let mut work_items = Vec::with_capacity(total_work_item_count);
let mut tokens = Vec::new();
@ -1253,7 +1272,8 @@ fn start_executing_work(sess: &Session,
} else {
shared_emitter.fatal("failed to acquire jobserver token");
drop(trans_worker_send.send(Message::CheckErrorMessages));
return
// Exit the coordinator thread
panic!()
}
}
@ -1269,21 +1289,42 @@ fn start_executing_work(sess: &Session,
//
// Note that if the thread failed that means it panicked, so we
// abort immediately.
Message::Done { success: true } => {
Message::Done { result: Ok(compiled_module) } => {
drop(tokens.pop());
running -= 1;
drop(trans_worker_send.send(Message::CheckErrorMessages));
match compiled_module.kind {
ModuleKind::Regular => {
compiled_modules.push(compiled_module);
}
ModuleKind::Metadata => {
assert!(compiled_metadata_module.is_none());
compiled_metadata_module = Some(compiled_module);
}
ModuleKind::Allocator => {
assert!(compiled_allocator_module.is_none());
compiled_allocator_module = Some(compiled_module);
}
}
}
Message::Done { success: false } => {
Message::Done { result: Err(()) } => {
shared_emitter.fatal("aborting due to worker thread panic");
drop(trans_worker_send.send(Message::CheckErrorMessages));
return
// Exit the coordinator thread
panic!()
}
msg @ Message::CheckErrorMessages => {
bug!("unexpected message: {:?}", msg);
}
}
}
CompiledModules {
modules: compiled_modules,
metadata_module: compiled_metadata_module.unwrap(),
allocator_module: compiled_allocator_module,
}
})
}
@ -1297,17 +1338,22 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) {
// we exit.
struct Bomb {
coordinator_send: Sender<Message>,
success: bool,
result: Option<CompiledModule>,
}
impl Drop for Bomb {
fn drop(&mut self) {
drop(self.coordinator_send.send(Message::Done { success: self.success }));
let result = match self.result.take() {
Some(compiled_module) => Ok(compiled_module),
None => Err(())
};
drop(self.coordinator_send.send(Message::Done { result }));
}
}
let mut bomb = Bomb {
coordinator_send: cgcx.coordinator_send.clone(),
success: false,
result: None,
};
// Execute the work itself, and if it finishes successfully then flag
@ -1323,8 +1369,7 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) {
// we just ignore the result and then send off our message saying that
// we're done, which if `execute_work_item` failed is unlikely to be
// seen by the main thread, but hey we might as well try anyway.
drop(execute_work_item(&cgcx, work).is_err());
bomb.success = true;
bomb.result = Some(execute_work_item(&cgcx, work).unwrap());
});
}

View file

@ -27,6 +27,7 @@ use super::OngoingCrateTranslation;
use super::ModuleLlvm;
use super::ModuleSource;
use super::ModuleTranslation;
use super::ModuleKind;
use assert_module_sources;
use back::link;
@ -952,6 +953,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
llcx: metadata_llcx,
llmod: metadata_llmod,
}),
kind: ModuleKind::Metadata,
};
let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
@ -961,7 +963,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
!tcx.sess.opts.output_types.should_trans() {
let empty_exported_symbols = ExportedSymbols::empty();
let linker_info = LinkerInfo::new(&shared_ccx, &empty_exported_symbols);
return OngoingCrateTranslation {
let crate_translation = OngoingCrateTranslation {
crate_name: tcx.crate_name(LOCAL_CRATE),
link: link_meta,
metadata: metadata,
@ -970,12 +972,18 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
linker_info: linker_info,
windows_subsystem: None,
no_integrated_as: false,
result: ::std::cell::RefCell::new(Some(::back::write::RunLLVMPassesResult {
modules: vec![],
metadata_module: metadata_module,
allocator_module: None,
})),
result: ::std::cell::RefCell::new(None),
};
::back::write::run_passes(tcx.sess,
&crate_translation,
vec![],
metadata_module,
None,
&output_filenames.outputs,
output_filenames);
return crate_translation;
}
let exported_symbols = Arc::new(ExportedSymbols::compute(tcx,
@ -1047,7 +1055,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let module = ModuleTranslation {
name: cgu_name,
symbol_name_hash,
source: ModuleSource::Preexisting(buf.clone())
source: ModuleSource::Preexisting(buf.clone()),
kind: ModuleKind::Regular,
};
return (Stats::default(), module);
}
@ -1108,7 +1117,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: ModuleSource::Translated(ModuleLlvm {
llcx: ccx.llcx(),
llmod: ccx.llmod(),
})
}),
kind: ModuleKind::Regular,
}
};
@ -1196,6 +1206,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
name: link::ALLOCATOR_MODULE_NAME.to_string(),
symbol_name_hash: 0, // we always rebuild allocator shims
source: ModuleSource::Translated(modules),
kind: ModuleKind::Allocator,
})
}
});

View file

@ -135,7 +135,6 @@ mod type_;
mod type_of;
mod value;
#[derive(Clone)]
pub struct ModuleTranslation {
/// The name of the module. When the crate may be saved between
/// compilations, incremental compilation requires that name be
@ -145,6 +144,58 @@ pub struct ModuleTranslation {
pub name: String,
pub symbol_name_hash: u64,
pub source: ModuleSource,
pub kind: ModuleKind,
}
#[derive(Copy, Clone, Debug)]
pub enum ModuleKind {
Regular,
Metadata,
Allocator,
}
impl ModuleTranslation {
pub fn into_compiled_module(self, emit_obj: bool, emit_bc: bool) -> CompiledModule {
let pre_existing = match self.source {
ModuleSource::Preexisting(_) => true,
ModuleSource::Translated(_) => false,
};
CompiledModule {
name: self.name.clone(),
kind: self.kind,
symbol_name_hash: self.symbol_name_hash,
pre_existing,
emit_obj,
emit_bc,
}
}
}
impl Drop for ModuleTranslation {
fn drop(&mut self) {
match self.source {
ModuleSource::Preexisting(_) => {
// Nothing to dispose.
},
ModuleSource::Translated(llvm) => {
unsafe {
llvm::LLVMDisposeModule(llvm.llmod);
llvm::LLVMContextDispose(llvm.llcx);
}
},
}
}
}
#[derive(Debug)]
pub struct CompiledModule {
pub name: String,
pub kind: ModuleKind,
pub symbol_name_hash: u64,
pub pre_existing: bool,
pub emit_obj: bool,
pub emit_bc: bool,
}
#[derive(Clone)]
@ -156,7 +207,7 @@ pub enum ModuleSource {
Translated(ModuleLlvm),
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub struct ModuleLlvm {
pub llcx: llvm::ContextRef,
pub llmod: llvm::ModuleRef,
@ -167,9 +218,9 @@ unsafe impl Sync for ModuleTranslation { }
pub struct CrateTranslation {
pub crate_name: Symbol,
pub modules: Vec<ModuleTranslation>,
pub metadata_module: ModuleTranslation,
pub allocator_module: Option<ModuleTranslation>,
pub modules: Vec<CompiledModule>,
pub metadata_module: CompiledModule,
pub allocator_module: Option<CompiledModule>,
pub link: rustc::middle::cstore::LinkMeta,
pub metadata: rustc::middle::cstore::EncodedMetadata,
pub exported_symbols: Arc<back::symbol_export::ExportedSymbols>,
@ -189,7 +240,7 @@ pub struct OngoingCrateTranslation {
pub no_integrated_as: bool,
// This will be replaced by a Future.
pub result: ::std::cell::RefCell<Option<back::write::RunLLVMPassesResult>>,
pub result: ::std::cell::RefCell<Option<back::write::CompiledModules>>,
}
impl OngoingCrateTranslation {