rustc_codegen_ssa/back/
lto.rs

1use std::ffi::CString;
2use std::sync::Arc;
3
4use rustc_ast::expand::allocator::AllocatorKind;
5use rustc_data_structures::memmap::Mmap;
6use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
7use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel};
8use rustc_middle::ty::TyCtxt;
9use rustc_session::config::{CrateType, Lto};
10use tracing::info;
11
12use crate::back::symbol_export::{self, allocator_shim_symbols, symbol_name_for_instance_in_crate};
13use crate::back::write::CodegenContext;
14use crate::base::allocator_kind_for_codegen;
15use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro};
16use crate::traits::*;
17
18pub struct ThinModule<B: WriteBackendMethods> {
19    pub shared: Arc<ThinShared<B>>,
20    pub idx: usize,
21}
22
23impl<B: WriteBackendMethods> ThinModule<B> {
24    pub fn name(&self) -> &str {
25        self.shared.module_names[self.idx].to_str().unwrap()
26    }
27
28    pub fn cost(&self) -> u64 {
29        // Yes, that's correct, we're using the size of the bytecode as an
30        // indicator for how costly this codegen unit is.
31        self.data().len() as u64
32    }
33
34    pub fn data(&self) -> &[u8] {
35        let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data());
36        a.unwrap_or_else(|| {
37            let len = self.shared.thin_buffers.len();
38            self.shared.serialized_modules[self.idx - len].data()
39        })
40    }
41}
42
43pub struct ThinShared<B: WriteBackendMethods> {
44    pub data: B::ThinData,
45    pub thin_buffers: Vec<B::ThinBuffer>,
46    pub serialized_modules: Vec<SerializedModule<B::ModuleBuffer>>,
47    pub module_names: Vec<CString>,
48}
49
50pub enum SerializedModule<M: ModuleBufferMethods> {
51    Local(M),
52    FromRlib(Vec<u8>),
53    FromUncompressedFile(Mmap),
54}
55
56impl<M: ModuleBufferMethods> SerializedModule<M> {
57    pub fn data(&self) -> &[u8] {
58        match *self {
59            SerializedModule::Local(ref m) => m.data(),
60            SerializedModule::FromRlib(ref m) => m,
61            SerializedModule::FromUncompressedFile(ref m) => m,
62        }
63    }
64}
65
66fn crate_type_allows_lto(crate_type: CrateType) -> bool {
67    match crate_type {
68        CrateType::Executable
69        | CrateType::Dylib
70        | CrateType::Staticlib
71        | CrateType::Cdylib
72        | CrateType::ProcMacro
73        | CrateType::Sdylib => true,
74        CrateType::Rlib => false,
75    }
76}
77
78pub(super) fn exported_symbols_for_lto(
79    tcx: TyCtxt<'_>,
80    each_linked_rlib_for_lto: &[CrateNum],
81) -> Vec<String> {
82    let export_threshold = match tcx.sess.lto() {
83        // We're just doing LTO for our one crate
84        Lto::ThinLocal => SymbolExportLevel::Rust,
85
86        // We're doing LTO for the entire crate graph
87        Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&tcx.crate_types()),
88
89        Lto::No => return vec![],
90    };
91
92    let copy_symbols = |cnum| {
93        tcx.exported_non_generic_symbols(cnum)
94            .iter()
95            .chain(tcx.exported_generic_symbols(cnum))
96            .filter_map(|&(s, info): &(ExportedSymbol<'_>, SymbolExportInfo)| {
97                if info.level.is_below_threshold(export_threshold) || info.used {
98                    Some(symbol_name_for_instance_in_crate(tcx, s, cnum))
99                } else if export_threshold == SymbolExportLevel::C
100                    && info.rustc_std_internal_symbol
101                    && let Some(AllocatorKind::Default) = allocator_kind_for_codegen(tcx)
102                {
103                    // Export the __rdl_* exports for usage by the allocator shim when not using
104                    // #[global_allocator]. Most of the conditions above are only used to avoid
105                    // unnecessary expensive symbol_name_for_instance_in_crate calls.
106                    let sym = symbol_name_for_instance_in_crate(tcx, s, cnum);
107                    if sym.contains("__rdl_") || sym.contains("__rg_oom") {
108                        Some(sym)
109                    } else {
110                        None
111                    }
112                } else {
113                    None
114                }
115            })
116            .collect::<Vec<_>>()
117    };
118    let mut symbols_below_threshold = {
119        let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
120        copy_symbols(LOCAL_CRATE)
121    };
122    info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
123
124    // If we're performing LTO for the entire crate graph, then for each of our
125    // upstream dependencies, include their exported symbols.
126    if tcx.sess.lto() != Lto::ThinLocal {
127        for &cnum in each_linked_rlib_for_lto {
128            let _timer = tcx.prof.generic_activity("lto_generate_symbols_below_threshold");
129            symbols_below_threshold.extend(copy_symbols(cnum));
130        }
131    }
132
133    // Mark allocator shim symbols as exported only if they were generated.
134    if export_threshold == SymbolExportLevel::Rust && allocator_kind_for_codegen(tcx).is_some() {
135        symbols_below_threshold.extend(allocator_shim_symbols(tcx).map(|(name, _kind)| name));
136    }
137
138    symbols_below_threshold
139}
140
141pub(super) fn check_lto_allowed<B: WriteBackendMethods>(cgcx: &CodegenContext<B>) {
142    if cgcx.lto == Lto::ThinLocal {
143        // Crate local LTO is always allowed
144        return;
145    }
146
147    let dcx = cgcx.create_dcx();
148
149    // Make sure we actually can run LTO
150    for crate_type in cgcx.crate_types.iter() {
151        if !crate_type_allows_lto(*crate_type) {
152            dcx.handle().emit_fatal(LtoDisallowed);
153        } else if *crate_type == CrateType::Dylib {
154            if !cgcx.opts.unstable_opts.dylib_lto {
155                dcx.handle().emit_fatal(LtoDylib);
156            }
157        } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto {
158            dcx.handle().emit_fatal(LtoProcMacro);
159        }
160    }
161
162    if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
163        dcx.handle().emit_fatal(DynamicLinkingWithLTO);
164    }
165}