use attributes;
use back::bytecode::{self, RLIB_BYTECODE_EXTENSION};
- use back::lto::{self, ModuleBuffer, ThinBuffer, SerializedModule};
+ use back::lto::{self, ThinBuffer, SerializedModule};
use back::link::{self, get_linker, remove};
-use back::command::Command;
-use back::linker::LinkerInfo;
-use back::symbol_export::ExportedSymbols;
use base;
use consts;
use memmap;
use rustc_fs_util::{path2cstr, link_or_copy};
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_data_structures::svh::Svh;
+use rustc_codegen_utils::command::Command;
+use rustc_codegen_utils::linker::LinkerInfo;
+use rustc_codegen_utils::symbol_export::ExportedSymbols;
use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId};
use errors::emitter::{Emitter};
use syntax::attr;
// Some options cause LLVM bitcode to be emitted, which uses ThinLTOBuffers, so we need
// to make sure we run LLVM's NameAnonGlobals pass when emitting bitcode; otherwise
// we'll get errors in LLVM.
- let using_thin_buffers = llvm::LLVMRustThinLTOAvailable() && (config.emit_bc
- || config.obj_is_bitcode || config.emit_bc_compressed || config.embed_bitcode);
+ let using_thin_buffers = config.emit_bc || config.obj_is_bitcode
+ || config.emit_bc_compressed || config.embed_bitcode;
let mut have_name_anon_globals_pass = false;
if !config.no_prepopulate_passes {
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
if write_bc || config.emit_bc_compressed || config.embed_bitcode {
- let thin;
- let old;
- let data = if llvm::LLVMRustThinLTOAvailable() {
- thin = ThinBuffer::new(llmod);
- thin.data()
- } else {
- old = ModuleBuffer::new(llmod);
- old.data()
- };
+ let thin = ThinBuffer::new(llmod);
+ let data = thin.data();
timeline.record("make-bc");
if write_bc {
// builds we don't actually want to LTO the allocator modules if
// it shows up. This is due to various linker shenanigans that
// we'll encounter later.
- //
- // Additionally here's where we also factor in the current LLVM
- // version. If it doesn't support ThinLTO we skip this.
Lto::ThinLocal => {
- module.kind != ModuleKind::Allocator &&
- unsafe { llvm::LLVMRustThinLTOAvailable() }
+ module.kind != ModuleKind::Allocator
}
};
},
CodegenComplete,
CodegenItem,
+ CodegenAborted,
}
struct Diagnostic {
let mut needs_lto = Vec::new();
let mut lto_import_only_modules = Vec::new();
let mut started_lto = false;
+ let mut codegen_aborted = false;
// This flag tracks whether all items have gone through codegens
let mut codegen_done = false;
let mut llvm_start_time = None;
// Run the message loop while there's still anything that needs message
- // processing:
+ // processing. Note that as soon as codegen is aborted we simply want to
+ // wait for all existing work to finish, so many of the conditions here
+ // only apply if codegen hasn't been aborted as they represent pending
+ // work to be done.
while !codegen_done ||
- work_items.len() > 0 ||
running > 0 ||
- needs_lto.len() > 0 ||
- lto_import_only_modules.len() > 0 ||
- main_thread_worker_state != MainThreadWorkerState::Idle {
+ (!codegen_aborted && (
+ work_items.len() > 0 ||
+ needs_lto.len() > 0 ||
+ lto_import_only_modules.len() > 0 ||
+ main_thread_worker_state != MainThreadWorkerState::Idle
+ ))
+ {
// While there are still CGUs to be codegened, the coordinator has
// to decide how to utilize the compiler processes implicit Token:
spawn_work(cgcx, item);
}
}
+ } else if codegen_aborted {
+ // don't queue up any more work if codegen was aborted, we're
+ // just waiting for our existing children to finish
} else {
// If we've finished everything related to normal codegen
// then it must be the case that we've got some LTO work to do.
// Spin up what work we can, only doing this while we've got available
// parallelism slots and work left to spawn.
- while work_items.len() > 0 && running < tokens.len() {
+ while !codegen_aborted && work_items.len() > 0 && running < tokens.len() {
let (item, _) = work_items.pop().unwrap();
maybe_start_llvm_timer(cgcx.config(item.module_kind()),
if !cgcx.opts.debugging_opts.no_parallel_llvm {
helper.request_token();
}
+ assert!(!codegen_aborted);
assert_eq!(main_thread_worker_state,
MainThreadWorkerState::Codegenning);
main_thread_worker_state = MainThreadWorkerState::Idle;
Message::CodegenComplete => {
codegen_done = true;
+ assert!(!codegen_aborted);
assert_eq!(main_thread_worker_state,
MainThreadWorkerState::Codegenning);
main_thread_worker_state = MainThreadWorkerState::Idle;
}
+ // If codegen is aborted that means translation was aborted due
+ // to some normal-ish compiler error. In this situation we want
+ // to exit as soon as possible, but we want to make sure all
+ // existing work has finished. Flag codegen as being done, and
+ // then conditions above will ensure no more work is spawned but
+ // we'll keep executing this loop until `running` hits 0.
+ Message::CodegenAborted => {
+ assert!(!codegen_aborted);
+ codegen_done = true;
+ codegen_aborted = true;
+ assert_eq!(main_thread_worker_state,
+ MainThreadWorkerState::Codegenning);
+ }
+
// If a thread exits successfully then we drop a token associated
// with that worker and update our `running` count. We may later
// re-acquire a token to continue running more work. We may also not
drop(self.coordinator_send.send(Box::new(Message::CodegenComplete)));
}
+ /// Consume this context indicating that codegen was entirely aborted, and
+ /// we need to exit as quickly as possible.
+ ///
+ /// This method blocks the current thread until all worker threads have
+ /// finished, and all worker threads should have exited or be real close to
+ /// exiting at this point.
+ pub fn codegen_aborted(self) {
+ // Signal to the coordinator it should spawn no more work and start
+ // shutdown.
+ drop(self.coordinator_send.send(Box::new(Message::CodegenAborted)));
+ drop(self.future.join());
+ }
+
pub fn check_for_errors(&self, sess: &Session) {
self.shared_emitter_main.check(sess, false);
}
}
}
+// impl Drop for OngoingCodegen {
+// fn drop(&mut self) {
+// }
+// }
+
pub(crate) fn submit_codegened_module_to_llvm(tcx: TyCtxt,
module: ModuleCodegen,
cost: u64) {
use builder::{Builder, MemFlags};
use callee;
use common::{C_bool, C_bytes_in_context, C_i32, C_usize};
-use rustc_mir::monomorphize::collector::{self, MonoItemCollectionMode};
use rustc_mir::monomorphize::item::DefPathBasedNames;
use common::{C_struct_in_context, C_array, val_ty};
use consts;
use meth;
use mir;
use monomorphize::Instance;
-use monomorphize::partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt};
+use monomorphize::partitioning::{CodegenUnit, CodegenUnitExt};
use rustc_codegen_utils::symbol_names_test;
use time_graph;
-use mono_item::{MonoItem, BaseMonoItemExt, MonoItemExt};
+use mono_item::{MonoItem, MonoItemExt};
use type_::Type;
use type_of::LayoutLlvmExt;
-use rustc::util::nodemap::{FxHashMap, DefIdSet};
+use rustc::util::nodemap::FxHashMap;
use CrateInfo;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_data_structures::sync::Lrc;
use std::any::Any;
+use std::cmp;
use std::ffi::CString;
-use std::sync::Arc;
-use std::time::{Instant, Duration};
use std::i32;
-use std::cmp;
+use std::ops::{Deref, DerefMut};
use std::sync::mpsc;
+use std::time::{Instant, Duration};
use syntax_pos::Span;
use syntax_pos::symbol::InternedString;
use syntax::attr;
// regions must appear in the argument
// listing.
let main_ret_ty = cx.tcx.erase_regions(
- &main_ret_ty.no_late_bound_regions().unwrap(),
+ &main_ret_ty.no_bound_vars().unwrap(),
);
if declare::get_defined_value(cx, "main").is_some() {
{
check_for_rustc_errors_attr(tcx);
- if let Some(true) = tcx.sess.opts.debugging_opts.thinlto {
- if unsafe { !llvm::LLVMRustThinLTOAvailable() } {
- tcx.sess.fatal("this compiler's LLVM does not support ThinLTO");
- }
- }
-
- if (tcx.sess.opts.debugging_opts.pgo_gen.is_some() ||
- !tcx.sess.opts.debugging_opts.pgo_use.is_empty()) &&
- unsafe { !llvm::LLVMRustPGOAvailable() }
- {
- tcx.sess.fatal("this compiler's LLVM does not support PGO");
- }
-
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
// Codegen the metadata.
metadata,
rx,
codegen_units.len());
+ let ongoing_codegen = AbortCodegenOnDrop(Some(ongoing_codegen));
// Codegen an allocator shim, if necessary.
//
ongoing_codegen.check_for_errors(tcx.sess);
assert_and_save_dep_graph(tcx);
- ongoing_codegen
+ ongoing_codegen.into_inner()
}
-fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
- time(tcx.sess,
- "assert dep graph",
- || rustc_incremental::assert_dep_graph(tcx));
-
- time(tcx.sess,
- "serialize dep graph",
- || rustc_incremental::save_dep_graph(tcx));
+/// A curious wrapper structure whose only purpose is to call `codegen_aborted`
+/// when it's dropped abnormally.
+///
+/// In the process of working on rust-lang/rust#55238 a mysterious segfault was
+/// stumbled upon. The segfault was never reproduced locally, but it was
+/// suspected to be releated to the fact that codegen worker threads were
+/// sticking around by the time the main thread was exiting, causing issues.
+///
+/// This structure is an attempt to fix that issue where the `codegen_aborted`
+/// message will block until all workers have finished. This should ensure that
+/// even if the main codegen thread panics we'll wait for pending work to
+/// complete before returning from the main thread, hopefully avoiding
+/// segfaults.
+///
+/// If you see this comment in the code, then it means that this workaround
+/// worked! We may yet one day track down the mysterious cause of that
+/// segfault...
+struct AbortCodegenOnDrop(Option<OngoingCodegen>);
+
+impl AbortCodegenOnDrop {
+ fn into_inner(mut self) -> OngoingCodegen {
+ self.0.take().unwrap()
+ }
}
-fn collect_and_partition_mono_items<'a, 'tcx>(
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- cnum: CrateNum,
-) -> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>)
-{
- assert_eq!(cnum, LOCAL_CRATE);
-
- let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items {
- Some(ref s) => {
- let mode_string = s.to_lowercase();
- let mode_string = mode_string.trim();
- if mode_string == "eager" {
- MonoItemCollectionMode::Eager
- } else {
- if mode_string != "lazy" {
- let message = format!("Unknown codegen-item collection mode '{}'. \
- Falling back to 'lazy' mode.",
- mode_string);
- tcx.sess.warn(&message);
- }
-
- MonoItemCollectionMode::Lazy
- }
- }
- None => {
- if tcx.sess.opts.cg.link_dead_code {
- MonoItemCollectionMode::Eager
- } else {
- MonoItemCollectionMode::Lazy
- }
- }
- };
-
- let (items, inlining_map) =
- time(tcx.sess, "monomorphization collection", || {
- collector::collect_crate_mono_items(tcx, collection_mode)
- });
-
- tcx.sess.abort_if_errors();
-
- ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter());
-
- let strategy = if tcx.sess.opts.incremental.is_some() {
- PartitioningStrategy::PerModule
- } else {
- PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units())
- };
-
- let codegen_units = time(tcx.sess, "codegen unit partitioning", || {
- partitioning::partition(tcx,
- items.iter().cloned(),
- strategy,
- &inlining_map)
- .into_iter()
- .map(Arc::new)
- .collect::<Vec<_>>()
- });
-
- let mono_items: DefIdSet = items.iter().filter_map(|mono_item| {
- match *mono_item {
- MonoItem::Fn(ref instance) => Some(instance.def_id()),
- MonoItem::Static(def_id) => Some(def_id),
- _ => None,
- }
- }).collect();
-
- if tcx.sess.opts.debugging_opts.print_mono_items.is_some() {
- let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
-
- for cgu in &codegen_units {
- for (&mono_item, &linkage) in cgu.items() {
- item_to_cgus.entry(mono_item)
- .or_default()
- .push((cgu.name().clone(), linkage));
- }
- }
+impl Deref for AbortCodegenOnDrop {
+ type Target = OngoingCodegen;
- let mut item_keys: Vec<_> = items
- .iter()
- .map(|i| {
- let mut output = i.to_string(tcx);
- output.push_str(" @@");
- let mut empty = Vec::new();
- let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
- cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone());
- cgus.dedup();
- for &(ref cgu_name, (linkage, _)) in cgus.iter() {
- output.push_str(" ");
- output.push_str(&cgu_name.as_str());
-
- let linkage_abbrev = match linkage {
- Linkage::External => "External",
- Linkage::AvailableExternally => "Available",
- Linkage::LinkOnceAny => "OnceAny",
- Linkage::LinkOnceODR => "OnceODR",
- Linkage::WeakAny => "WeakAny",
- Linkage::WeakODR => "WeakODR",
- Linkage::Appending => "Appending",
- Linkage::Internal => "Internal",
- Linkage::Private => "Private",
- Linkage::ExternalWeak => "ExternalWeak",
- Linkage::Common => "Common",
- };
-
- output.push_str("[");
- output.push_str(linkage_abbrev);
- output.push_str("]");
- }
- output
- })
- .collect();
+ fn deref(&self) -> &OngoingCodegen {
+ self.0.as_ref().unwrap()
+ }
+}
- item_keys.sort();
+impl DerefMut for AbortCodegenOnDrop {
+ fn deref_mut(&mut self) -> &mut OngoingCodegen {
+ self.0.as_mut().unwrap()
+ }
+}
- for item in item_keys {
- println!("MONO_ITEM {}", item);
+impl Drop for AbortCodegenOnDrop {
+ fn drop(&mut self) {
+ if let Some(codegen) = self.0.take() {
+ codegen.codegen_aborted();
}
}
+}
- (Arc::new(mono_items), Arc::new(codegen_units))
+fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
+ time(tcx.sess,
+ "assert dep graph",
+ || rustc_incremental::assert_dep_graph(tcx));
+
+ time(tcx.sess,
+ "serialize dep graph",
+ || rustc_incremental::save_dep_graph(tcx));
}
impl CrateInfo {
}
}
-fn is_codegened_item(tcx: TyCtxt, id: DefId) -> bool {
- let (all_mono_items, _) =
- tcx.collect_and_partition_mono_items(LOCAL_CRATE);
- all_mono_items.contains(&id)
-}
-
fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
cgu_name: InternedString)
-> Stats {
}
}
-pub fn provide(providers: &mut Providers) {
- providers.collect_and_partition_mono_items =
- collect_and_partition_mono_items;
-
- providers.is_codegened_item = is_codegened_item;
-
- providers.codegen_unit = |tcx, name| {
- let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
- all.iter()
- .find(|cgu| *cgu.name() == name)
- .cloned()
- .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name))
- };
-
- provide_extern(providers);
-}
-
-pub fn provide_extern(providers: &mut Providers) {
+pub fn provide_both(providers: &mut Providers) {
providers.dllimport_foreign_items = |tcx, krate| {
let module_map = tcx.foreign_modules(krate);
let module_map = module_map.iter()