Skip to main content

rustc_codegen_ssa/back/
lto.rs

1use std::ffi::CString;
2use std::fs;
3use std::path::Path;
4use std::sync::Arc;
5
6use rustc_data_structures::memmap::Mmap;
7use rustc_errors::DiagCtxtHandle;
8use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
9use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel};
10use rustc_middle::ty::TyCtxt;
11use rustc_session::config::{CrateType, Lto};
12use tracing::info;
13
14use crate::back::symbol_export::{self, allocator_shim_symbols, symbol_name_for_instance_in_crate};
15use crate::back::write::CodegenContext;
16use crate::base::allocator_kind_for_codegen;
17use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro};
18use crate::traits::*;
19
20pub struct ThinModule<B: WriteBackendMethods> {
21    pub shared: Arc<ThinShared<B>>,
22    pub idx: usize,
23}
24
25impl<B: WriteBackendMethods> ThinModule<B> {
26    pub fn name(&self) -> &str {
27        self.shared.module_names[self.idx].to_str().unwrap()
28    }
29
30    pub fn cost(&self) -> u64 {
31        // Yes, that's correct, we're using the size of the bytecode as an
32        // indicator for how costly this codegen unit is.
33        self.data().len() as u64
34    }
35
36    pub fn data(&self) -> &[u8] {
37        self.shared.modules[self.idx].data()
38    }
39}
40
41pub struct ThinShared<B: WriteBackendMethods> {
42    pub data: B::ThinData,
43    pub modules: Vec<SerializedModule<B::ModuleBuffer>>,
44    pub module_names: Vec<CString>,
45}
46
47pub enum SerializedModule<M: ModuleBufferMethods> {
48    Local(M),
49    FromRlib(Vec<u8>),
50    FromUncompressedFile(Mmap),
51}
52
53impl<M: ModuleBufferMethods> SerializedModule<M> {
54    pub fn from_file(bc_path: &Path) -> Self {
55        let file = fs::File::open(&bc_path).unwrap_or_else(|e| {
56            {
    ::core::panicking::panic_fmt(format_args!("failed to open LTO bitcode file `{0}`: {1}",
            bc_path.display(), e));
}panic!("failed to open LTO bitcode file `{}`: {}", bc_path.display(), e)
57        });
58
59        let mmap = unsafe {
60            Mmap::map(file).unwrap_or_else(|e| {
61                {
    ::core::panicking::panic_fmt(format_args!("failed to mmap LTO bitcode file `{0}`: {1}",
            bc_path.display(), e));
}panic!("failed to mmap LTO bitcode file `{}`: {}", bc_path.display(), e)
62            })
63        };
64        SerializedModule::FromUncompressedFile(mmap)
65    }
66
67    pub fn data(&self) -> &[u8] {
68        match *self {
69            SerializedModule::Local(ref m) => m.data(),
70            SerializedModule::FromRlib(ref m) => m,
71            SerializedModule::FromUncompressedFile(ref m) => m,
72        }
73    }
74}
75
76fn crate_type_allows_lto(crate_type: CrateType) -> bool {
77    match crate_type {
78        CrateType::Executable
79        | CrateType::Dylib
80        | CrateType::StaticLib
81        | CrateType::Cdylib
82        | CrateType::ProcMacro
83        | CrateType::Sdylib => true,
84        CrateType::Rlib => false,
85    }
86}
87
88pub(super) fn exported_symbols_for_lto(
89    tcx: TyCtxt<'_>,
90    each_linked_rlib_for_lto: &[CrateNum],
91) -> Vec<String> {
92    let export_threshold = match tcx.sess.lto() {
93        // We're just doing LTO for our one crate
94        Lto::ThinLocal => SymbolExportLevel::Rust,
95
96        // We're doing LTO for the entire crate graph
97        Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&tcx.crate_types()),
98
99        Lto::No => return ::alloc::vec::Vec::new()vec![],
100    };
101
102    let copy_symbols = |cnum| {
103        tcx.exported_non_generic_symbols(cnum)
104            .iter()
105            .chain(tcx.exported_generic_symbols(cnum))
106            .filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| {
107                if info.level.is_below_threshold(export_threshold) || info.used {
108                    Some(symbol_name_for_instance_in_crate(tcx, s, cnum))
109                } else {
110                    None
111                }
112            })
113            .collect::<Vec<_>>()
114    };
115    let mut symbols_below_threshold = {
116        let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
117        copy_symbols(LOCAL_CRATE)
118    };
119    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/lto.rs:119",
                        "rustc_codegen_ssa::back::lto", ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/lto.rs"),
                        ::tracing_core::__macro_support::Option::Some(119u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::lto"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("{0} symbols to preserve in this crate",
                                                    symbols_below_threshold.len()) as &dyn Value))])
            });
    } else { ; }
};info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
120
121    // If we're performing LTO for the entire crate graph, then for each of our
122    // upstream dependencies, include their exported symbols.
123    for &cnum in each_linked_rlib_for_lto {
124        let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
125        symbols_below_threshold.extend(copy_symbols(cnum));
126    }
127
128    // Mark allocator shim symbols as exported only if they were generated.
129    if export_threshold == SymbolExportLevel::Rust
130        && let Some(kind) = allocator_kind_for_codegen(tcx)
131    {
132        symbols_below_threshold.extend(allocator_shim_symbols(tcx, kind).map(|(name, _kind)| name));
133    }
134
135    symbols_below_threshold
136}
137
138pub(super) fn check_lto_allowed(cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>) {
139    if cgcx.lto == Lto::ThinLocal {
140        // Crate local LTO is always allowed
141        return;
142    }
143
144    // Make sure we actually can run LTO
145    for crate_type in cgcx.crate_types.iter() {
146        if !crate_type_allows_lto(*crate_type) {
147            dcx.handle().emit_fatal(LtoDisallowed);
148        } else if *crate_type == CrateType::Dylib {
149            if !cgcx.dylib_lto {
150                dcx.handle().emit_fatal(LtoDylib);
151            }
152        } else if *crate_type == CrateType::ProcMacro && !cgcx.dylib_lto {
153            dcx.handle().emit_fatal(LtoProcMacro);
154        }
155    }
156
157    if cgcx.prefer_dynamic && !cgcx.dylib_lto {
158        dcx.handle().emit_fatal(DynamicLinkingWithLTO);
159    }
160}