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