| use std::ffi::CString; | |
| use std::sync::Arc; | |
| use rustc_data_structures::memmap::Mmap; | |
| use rustc_errors::DiagCtxtHandle; | |
| use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; | |
| use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel}; | |
| use rustc_middle::ty::TyCtxt; | |
| use rustc_session::config::{CrateType, Lto}; | |
| use tracing::info; | |
| use crate::back::symbol_export::{self, allocator_shim_symbols, symbol_name_for_instance_in_crate}; | |
| use crate::back::write::CodegenContext; | |
| use crate::base::allocator_kind_for_codegen; | |
| use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; | |
| use crate::traits::*; | |
| pub struct ThinModule<B: WriteBackendMethods> { | |
| pub shared: Arc<ThinShared<B>>, | |
| pub idx: usize, | |
| } | |
| impl<B: WriteBackendMethods> ThinModule<B> { | |
| pub fn name(&self) -> &str { | |
| self.shared.module_names[self.idx].to_str().unwrap() | |
| } | |
| pub fn cost(&self) -> u64 { | |
| // Yes, that's correct, we're using the size of the bytecode as an | |
| // indicator for how costly this codegen unit is. | |
| self.data().len() as u64 | |
| } | |
| pub fn data(&self) -> &[u8] { | |
| let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data()); | |
| a.unwrap_or_else(|| { | |
| let len = self.shared.thin_buffers.len(); | |
| self.shared.serialized_modules[self.idx - len].data() | |
| }) | |
| } | |
| } | |
| pub struct ThinShared<B: WriteBackendMethods> { | |
| pub data: B::ThinData, | |
| pub thin_buffers: Vec<B::ThinBuffer>, | |
| pub serialized_modules: Vec<SerializedModule<B::ModuleBuffer>>, | |
| pub module_names: Vec<CString>, | |
| } | |
| pub enum SerializedModule<M: ModuleBufferMethods> { | |
| Local(M), | |
| FromRlib(Vec<u8>), | |
| FromUncompressedFile(Mmap), | |
| } | |
| impl<M: ModuleBufferMethods> SerializedModule<M> { | |
| pub fn data(&self) -> &[u8] { | |
| match *self { | |
| SerializedModule::Local(ref m) => m.data(), | |
| SerializedModule::FromRlib(ref m) => m, | |
| SerializedModule::FromUncompressedFile(ref m) => m, | |
| } | |
| } | |
| } | |
| fn crate_type_allows_lto(crate_type: CrateType) -> bool { | |
| match crate_type { | |
| CrateType::Executable | |
| | CrateType::Dylib | |
| | CrateType::StaticLib | |
| | CrateType::Cdylib | |
| | CrateType::ProcMacro | |
| | CrateType::Sdylib => true, | |
| CrateType::Rlib => false, | |
| } | |
| } | |
| pub(super) fn exported_symbols_for_lto( | |
| tcx: TyCtxt<'_>, | |
| each_linked_rlib_for_lto: &[CrateNum], | |
| ) -> Vec<String> { | |
| let export_threshold = match tcx.sess.lto() { | |
| // We're just doing LTO for our one crate | |
| Lto::ThinLocal => SymbolExportLevel::Rust, | |
| // We're doing LTO for the entire crate graph | |
| Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&tcx.crate_types()), | |
| Lto::No => return vec![], | |
| }; | |
| let copy_symbols = |cnum| { | |
| tcx.exported_non_generic_symbols(cnum) | |
| .iter() | |
| .chain(tcx.exported_generic_symbols(cnum)) | |
| .filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| { | |
| if info.level.is_below_threshold(export_threshold) || info.used { | |
| Some(symbol_name_for_instance_in_crate(tcx, s, cnum)) | |
| } else { | |
| None | |
| } | |
| }) | |
| .collect::<Vec<_>>() | |
| }; | |
| let mut symbols_below_threshold = { | |
| let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold"); | |
| copy_symbols(LOCAL_CRATE) | |
| }; | |
| info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); | |
| // If we're performing LTO for the entire crate graph, then for each of our | |
| // upstream dependencies, include their exported symbols. | |
| if tcx.sess.lto() != Lto::ThinLocal { | |
| for &cnum in each_linked_rlib_for_lto { | |
| let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold"); | |
| symbols_below_threshold.extend(copy_symbols(cnum)); | |
| } | |
| } | |
| // Mark allocator shim symbols as exported only if they were generated. | |
| if export_threshold == SymbolExportLevel::Rust | |
| && let Some(kind) = allocator_kind_for_codegen(tcx) | |
| { | |
| symbols_below_threshold.extend(allocator_shim_symbols(tcx, kind).map(|(name, _kind)| name)); | |
| } | |
| symbols_below_threshold | |
| } | |
| pub(super) fn check_lto_allowed(cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>) { | |
| if cgcx.lto == Lto::ThinLocal { | |
| // Crate local LTO is always allowed | |
| return; | |
| } | |
| // Make sure we actually can run LTO | |
| for crate_type in cgcx.crate_types.iter() { | |
| if !crate_type_allows_lto(*crate_type) { | |
| dcx.handle().emit_fatal(LtoDisallowed); | |
| } else if *crate_type == CrateType::Dylib { | |
| if !cgcx.dylib_lto { | |
| dcx.handle().emit_fatal(LtoDylib); | |
| } | |
| } else if *crate_type == CrateType::ProcMacro && !cgcx.dylib_lto { | |
| dcx.handle().emit_fatal(LtoProcMacro); | |
| } | |
| } | |
| if cgcx.prefer_dynamic && !cgcx.dylib_lto { | |
| dcx.handle().emit_fatal(DynamicLinkingWithLTO); | |
| } | |
| } |