rustc_codegen_ssa/back/
lto.rs

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