]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_ssa/back/symbol_export.rs
Various minor/cosmetic improvements to code
[rust.git] / src / librustc_codegen_ssa / back / symbol_export.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc_data_structures::sync::Lrc;
12 use std::sync::Arc;
13
14 use rustc::ty::Instance;
15 use rustc::hir;
16 use rustc::hir::Node;
17 use rustc::hir::CodegenFnAttrFlags;
18 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
19 use rustc_data_structures::fingerprint::Fingerprint;
20 use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name};
21 use rustc::session::config;
22 use rustc::ty::{TyCtxt, SymbolName};
23 use rustc::ty::query::Providers;
24 use rustc::ty::subst::Substs;
25 use rustc::util::nodemap::{FxHashMap, DefIdMap};
26 use rustc_allocator::ALLOCATOR_METHODS;
27 use rustc_data_structures::indexed_vec::IndexVec;
28 use std::collections::hash_map::Entry::*;
29
30 pub type ExportedSymbols = FxHashMap<
31     CrateNum,
32     Arc<Vec<(String, SymbolExportLevel)>>,
33 >;
34
35 pub fn threshold(tcx: TyCtxt) -> SymbolExportLevel {
36     crates_export_threshold(&tcx.sess.crate_types.borrow())
37 }
38
39 fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel {
40     match crate_type {
41         config::CrateType::Executable |
42         config::CrateType::Staticlib  |
43         config::CrateType::ProcMacro  |
44         config::CrateType::Cdylib     => SymbolExportLevel::C,
45         config::CrateType::Rlib       |
46         config::CrateType::Dylib      => SymbolExportLevel::Rust,
47     }
48 }
49
50 pub fn crates_export_threshold(crate_types: &[config::CrateType]) -> SymbolExportLevel {
51     if crate_types.iter().any(|&crate_type|
52         crate_export_threshold(crate_type) == SymbolExportLevel::Rust)
53     {
54         SymbolExportLevel::Rust
55     } else {
56         SymbolExportLevel::C
57     }
58 }
59
60 fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
61                                              cnum: CrateNum)
62                                              -> Lrc<DefIdMap<SymbolExportLevel>>
63 {
64     assert_eq!(cnum, LOCAL_CRATE);
65
66     if !tcx.sess.opts.output_types.should_codegen() {
67         return Default::default();
68     }
69
70     // Check to see if this crate is a "special runtime crate". These
71     // crates, implementation details of the standard library, typically
72     // have a bunch of `pub extern` and `#[no_mangle]` functions as the
73     // ABI between them. We don't want their symbols to have a `C`
74     // export level, however, as they're just implementation details.
75     // Down below we'll hardwire all of the symbols to the `Rust` export
76     // level instead.
77     let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) ||
78         tcx.is_compiler_builtins(LOCAL_CRATE);
79
80     let mut reachable_non_generics: DefIdMap<_> = tcx.reachable_set(LOCAL_CRATE).0
81         .iter()
82         .filter_map(|&node_id| {
83             // We want to ignore some FFI functions that are not exposed from
84             // this crate. Reachable FFI functions can be lumped into two
85             // categories:
86             //
87             // 1. Those that are included statically via a static library
88             // 2. Those included otherwise (e.g., dynamically or via a framework)
89             //
90             // Although our LLVM module is not literally emitting code for the
91             // statically included symbols, it's an export of our library which
92             // needs to be passed on to the linker and encoded in the metadata.
93             //
94             // As a result, if this id is an FFI item (foreign item) then we only
95             // let it through if it's included statically.
96             match tcx.hir().get(node_id) {
97                 Node::ForeignItem(..) => {
98                     let def_id = tcx.hir().local_def_id(node_id);
99                     if tcx.is_statically_included_foreign_item(def_id) {
100                         Some(def_id)
101                     } else {
102                         None
103                     }
104                 }
105
106                 // Only consider nodes that actually have exported symbols.
107                 Node::Item(&hir::Item {
108                     node: hir::ItemKind::Static(..),
109                     ..
110                 }) |
111                 Node::Item(&hir::Item {
112                     node: hir::ItemKind::Fn(..), ..
113                 }) |
114                 Node::ImplItem(&hir::ImplItem {
115                     node: hir::ImplItemKind::Method(..),
116                     ..
117                 }) => {
118                     let def_id = tcx.hir().local_def_id(node_id);
119                     let generics = tcx.generics_of(def_id);
120                     if !generics.requires_monomorphization(tcx) &&
121                         // Functions marked with #[inline] are only ever codegened
122                         // with "internal" linkage and are never exported.
123                         !Instance::mono(tcx, def_id).def.requires_local(tcx) {
124                         Some(def_id)
125                     } else {
126                         None
127                     }
128                 }
129
130                 _ => None
131             }
132         })
133         .map(|def_id| {
134             let export_level = if special_runtime_crate {
135                 let name = tcx.symbol_name(Instance::mono(tcx, def_id)).as_str();
136                 // We can probably do better here by just ensuring that
137                 // it has hidden visibility rather than public
138                 // visibility, as this is primarily here to ensure it's
139                 // not stripped during LTO.
140                 //
141                 // In general though we won't link right if these
142                 // symbols are stripped, and LTO currently strips them.
143                 if &*name == "rust_eh_personality" ||
144                    &*name == "rust_eh_register_frames" ||
145                    &*name == "rust_eh_unregister_frames" {
146                     SymbolExportLevel::C
147                 } else {
148                     SymbolExportLevel::Rust
149                 }
150             } else {
151                 symbol_export_level(tcx, def_id)
152             };
153             debug!("EXPORTED SYMBOL (local): {} ({:?})",
154                    tcx.symbol_name(Instance::mono(tcx, def_id)),
155                    export_level);
156             (def_id, export_level)
157         })
158         .collect();
159
160     if let Some(id) = *tcx.sess.proc_macro_decls_static.get() {
161         let def_id = tcx.hir().local_def_id(id);
162         reachable_non_generics.insert(def_id, SymbolExportLevel::C);
163     }
164
165     if let Some(id) = *tcx.sess.plugin_registrar_fn.get() {
166         let def_id = tcx.hir().local_def_id(id);
167         reachable_non_generics.insert(def_id, SymbolExportLevel::C);
168     }
169
170     Lrc::new(reachable_non_generics)
171 }
172
173 fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
174                                                      def_id: DefId)
175                                                      -> bool {
176     let export_threshold = threshold(tcx);
177
178     if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) {
179         level.is_below_threshold(export_threshold)
180     } else {
181         false
182     }
183 }
184
185 fn is_reachable_non_generic_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
186                                                       def_id: DefId)
187                                                       -> bool {
188     tcx.reachable_non_generics(def_id.krate).contains_key(&def_id)
189 }
190
191 fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
192                                              cnum: CrateNum)
193                                              -> Arc<Vec<(ExportedSymbol<'tcx>,
194                                                          SymbolExportLevel)>>
195 {
196     assert_eq!(cnum, LOCAL_CRATE);
197
198     if !tcx.sess.opts.output_types.should_codegen() {
199         return Arc::new(vec![])
200     }
201
202     let mut symbols: Vec<_> = tcx.reachable_non_generics(LOCAL_CRATE)
203                                  .iter()
204                                  .map(|(&def_id, &level)| {
205                                     (ExportedSymbol::NonGeneric(def_id), level)
206                                  })
207                                  .collect();
208
209     if tcx.sess.entry_fn.borrow().is_some() {
210         let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new("main"));
211
212         symbols.push((exported_symbol, SymbolExportLevel::C));
213     }
214
215     if tcx.sess.allocator_kind.get().is_some() {
216         for method in ALLOCATOR_METHODS {
217             let symbol_name = format!("__rust_{}", method.name);
218             let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name));
219
220             symbols.push((exported_symbol, SymbolExportLevel::Rust));
221         }
222     }
223
224     if tcx.sess.opts.debugging_opts.pgo_gen.is_some() {
225         // These are weak symbols that point to the profile version and the
226         // profile name, which need to be treated as exported so LTO doesn't nix
227         // them.
228         const PROFILER_WEAK_SYMBOLS: [&str; 2] = [
229             "__llvm_profile_raw_version",
230             "__llvm_profile_filename",
231         ];
232
233         symbols.extend(PROFILER_WEAK_SYMBOLS.iter().map(|sym| {
234             let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(sym));
235             (exported_symbol, SymbolExportLevel::C)
236         }));
237     }
238
239     if tcx.sess.crate_types.borrow().contains(&config::CrateType::Dylib) {
240         let symbol_name = metadata_symbol_name(tcx);
241         let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name));
242
243         symbols.push((exported_symbol, SymbolExportLevel::Rust));
244     }
245
246     if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() {
247         use rustc::mir::mono::{Linkage, Visibility, MonoItem};
248         use rustc::ty::InstanceDef;
249
250         // Normally, we require that shared monomorphizations are not hidden,
251         // because if we want to re-use a monomorphization from a Rust dylib, it
252         // needs to be exported.
253         // However, on platforms that don't allow for Rust dylibs, having
254         // external linkage is enough for monomorphization to be linked to.
255         let need_visibility = tcx.sess.target.target.options.dynamic_linking &&
256                               !tcx.sess.target.target.options.only_cdylib;
257
258         let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
259
260         for (mono_item, &(linkage, visibility)) in cgus.iter()
261                                                        .flat_map(|cgu| cgu.items().iter()) {
262             if linkage != Linkage::External {
263                 // We can only re-use things with external linkage, otherwise
264                 // we'll get a linker error
265                 continue
266             }
267
268             if need_visibility && visibility == Visibility::Hidden {
269                 // If we potentially share things from Rust dylibs, they must
270                 // not be hidden
271                 continue
272             }
273
274             if let &MonoItem::Fn(Instance {
275                 def: InstanceDef::Item(def_id),
276                 substs,
277             }) = mono_item {
278                 if substs.types().next().is_some() {
279                     symbols.push((ExportedSymbol::Generic(def_id, substs),
280                                   SymbolExportLevel::Rust));
281                 }
282             }
283         }
284     }
285
286     // Sort so we get a stable incr. comp. hash.
287     symbols.sort_unstable_by(|&(ref symbol1, ..), &(ref symbol2, ..)| {
288         symbol1.compare_stable(tcx, symbol2)
289     });
290
291     Arc::new(symbols)
292 }
293
294 fn upstream_monomorphizations_provider<'a, 'tcx>(
295     tcx: TyCtxt<'a, 'tcx, 'tcx>,
296     cnum: CrateNum)
297     -> Lrc<DefIdMap<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>>
298 {
299     debug_assert!(cnum == LOCAL_CRATE);
300
301     let cnums = tcx.all_crate_nums(LOCAL_CRATE);
302
303     let mut instances: DefIdMap<FxHashMap<_, _>> = Default::default();
304
305     let cnum_stable_ids: IndexVec<CrateNum, Fingerprint> = {
306         let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO,
307                                                         cnums.len() + 1);
308
309         for &cnum in cnums.iter() {
310             cnum_stable_ids[cnum] = tcx.def_path_hash(DefId {
311                 krate: cnum,
312                 index: CRATE_DEF_INDEX,
313             }).0;
314         }
315
316         cnum_stable_ids
317     };
318
319     for &cnum in cnums.iter() {
320         for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
321             if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol {
322                 let substs_map = instances.entry(def_id).or_default();
323
324                 match substs_map.entry(substs) {
325                     Occupied(mut e) => {
326                         // If there are multiple monomorphizations available,
327                         // we select one deterministically.
328                         let other_cnum = *e.get();
329                         if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] {
330                             e.insert(cnum);
331                         }
332                     }
333                     Vacant(e) => {
334                         e.insert(cnum);
335                     }
336                 }
337             }
338         }
339     }
340
341     Lrc::new(instances.into_iter()
342                       .map(|(key, value)| (key, Lrc::new(value)))
343                       .collect())
344 }
345
346 fn upstream_monomorphizations_for_provider<'a, 'tcx>(
347     tcx: TyCtxt<'a, 'tcx, 'tcx>,
348     def_id: DefId)
349     -> Option<Lrc<FxHashMap<&'tcx Substs<'tcx>, CrateNum>>>
350 {
351     debug_assert!(!def_id.is_local());
352     tcx.upstream_monomorphizations(LOCAL_CRATE)
353        .get(&def_id)
354        .cloned()
355 }
356
357 fn is_unreachable_local_definition_provider(tcx: TyCtxt, def_id: DefId) -> bool {
358     if let Some(node_id) = tcx.hir().as_local_node_id(def_id) {
359         !tcx.reachable_set(LOCAL_CRATE).0.contains(&node_id)
360     } else {
361         bug!("is_unreachable_local_definition called with non-local DefId: {:?}",
362              def_id)
363     }
364 }
365
366 pub fn provide(providers: &mut Providers) {
367     providers.reachable_non_generics = reachable_non_generics_provider;
368     providers.is_reachable_non_generic = is_reachable_non_generic_provider_local;
369     providers.exported_symbols = exported_symbols_provider_local;
370     providers.upstream_monomorphizations = upstream_monomorphizations_provider;
371     providers.is_unreachable_local_definition = is_unreachable_local_definition_provider;
372 }
373
374 pub fn provide_extern(providers: &mut Providers) {
375     providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
376     providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider;
377 }
378
379 fn symbol_export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel {
380     // We export anything that's not mangled at the "C" layer as it probably has
381     // to do with ABI concerns. We do not, however, apply such treatment to
382     // special symbols in the standard library for various plumbing between
383     // core/std/allocators/etc. For example symbols used to hook up allocation
384     // are not considered for export
385     let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id);
386     let is_extern = codegen_fn_attrs.contains_extern_indicator();
387     let std_internal =
388         codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
389
390     if is_extern && !std_internal {
391         // Emscripten cannot export statics, so reduce their export level here
392         if tcx.sess.target.target.options.is_like_emscripten {
393             if let Some(Node::Item(&hir::Item {
394                 node: hir::ItemKind::Static(..),
395                 ..
396             })) = tcx.hir().get_if_local(sym_def_id) {
397                 return SymbolExportLevel::Rust;
398             }
399         }
400
401         SymbolExportLevel::C
402     } else {
403         SymbolExportLevel::Rust
404     }
405 }