diff options
| author | Irina Popa <irinagpopa@gmail.com> | 2018-05-08 16:10:16 +0300 |
|---|---|---|
| committer | Irina Popa <irinagpopa@gmail.com> | 2018-05-17 15:08:30 +0300 |
| commit | b63d7e2b1c4019e40051036bcb1fd5f254a8f6e2 (patch) | |
| tree | 314792e2f467d17181d29d4988550058197ac029 /src/librustc_codegen_utils | |
| parent | e3150564f889a3bad01795d9fcb31d4f14d58a99 (diff) | |
| download | rust-b63d7e2b1c4019e40051036bcb1fd5f254a8f6e2.tar.gz rust-b63d7e2b1c4019e40051036bcb1fd5f254a8f6e2.zip | |
Rename trans to codegen everywhere.
Diffstat (limited to 'src/librustc_codegen_utils')
| -rw-r--r-- | src/librustc_codegen_utils/Cargo.toml | 23 | ||||
| -rw-r--r-- | src/librustc_codegen_utils/codegen_backend.rs | 296 | ||||
| -rw-r--r-- | src/librustc_codegen_utils/lib.rs | 63 | ||||
| -rw-r--r-- | src/librustc_codegen_utils/link.rs | 191 | ||||
| -rw-r--r-- | src/librustc_codegen_utils/symbol_names.rs | 435 | ||||
| -rw-r--r-- | src/librustc_codegen_utils/symbol_names_test.rs | 79 |
6 files changed, 1087 insertions, 0 deletions
diff --git a/src/librustc_codegen_utils/Cargo.toml b/src/librustc_codegen_utils/Cargo.toml new file mode 100644 index 00000000000..690fb260390 --- /dev/null +++ b/src/librustc_codegen_utils/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_codegen_utils" +version = "0.0.0" + +[lib] +name = "rustc_codegen_utils" +path = "lib.rs" +crate-type = ["dylib"] +test = false + +[dependencies] +ar = "0.3.0" +flate2 = "1.0" +log = "0.4" + +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } +rustc = { path = "../librustc" } +rustc_target = { path = "../librustc_target" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_mir = { path = "../librustc_mir" } +rustc_incremental = { path = "../librustc_incremental" } diff --git a/src/librustc_codegen_utils/codegen_backend.rs b/src/librustc_codegen_utils/codegen_backend.rs new file mode 100644 index 00000000000..15aab680289 --- /dev/null +++ b/src/librustc_codegen_utils/codegen_backend.rs @@ -0,0 +1,296 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] +#![deny(warnings)] + +#![feature(box_syntax)] + +use std::any::Any; +use std::io::prelude::*; +use std::io::{self, Cursor}; +use std::fs::File; +use std::path::Path; +use std::sync::mpsc; + +use rustc_data_structures::owning_ref::OwningRef; +use rustc_data_structures::sync::Lrc; +use ar::{Archive, Builder, Header}; +use flate2::Compression; +use flate2::write::DeflateEncoder; + +use syntax::symbol::Symbol; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::session::{Session, CompileIncomplete}; +use rustc::session::config::{CrateType, OutputFilenames, PrintRequest}; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc::middle::cstore::EncodedMetadata; +use rustc::middle::cstore::MetadataLoader; +use rustc::dep_graph::DepGraph; +use rustc_target::spec::Target; +use rustc_data_structures::fx::FxHashMap; +use rustc_mir::monomorphize::collector; +use link::{build_link_meta, out_filename}; + +pub use rustc_data_structures::sync::MetadataRef; + +pub trait CodegenBackend { + fn init(&self, _sess: &Session) {} + fn print(&self, _req: PrintRequest, _sess: &Session) {} + fn target_features(&self, _sess: &Session) -> Vec<Symbol> { vec![] } + fn print_passes(&self) {} + fn print_version(&self) {} + fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] } + + fn metadata_loader(&self) -> Box<MetadataLoader + Sync>; + fn provide(&self, _providers: &mut Providers); + fn provide_extern(&self, _providers: &mut Providers); + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver<Box<Any + Send>> + ) -> Box<Any>; + + /// This is called on the returned `Box<Any>` from `codegen_backend` + /// + /// # Panics + /// + /// Panics when the passed `Box<Any>` was not returned by `codegen_backend`. + fn join_codegen_and_link( + &self, + ongoing_codegen: Box<Any>, + sess: &Session, + dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete>; +} + +pub struct DummyCodegenBackend; + +impl CodegenBackend for DummyCodegenBackend { + fn metadata_loader(&self) -> Box<MetadataLoader + Sync> { + box DummyMetadataLoader(()) + } + + fn provide(&self, _providers: &mut Providers) { + bug!("DummyCodegenBackend::provide"); + } + + fn provide_extern(&self, _providers: &mut Providers) { + bug!("DummyCodegenBackend::provide_extern"); + } + + fn codegen_crate<'a, 'tcx>( + &self, + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver<Box<Any + Send>> + ) -> Box<Any> { + bug!("DummyCodegenBackend::codegen_backend"); + } + + fn join_codegen_and_link( + &self, + _ongoing_codegen: Box<Any>, + _sess: &Session, + _dep_graph: &DepGraph, + _outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete> { + bug!("DummyCodegenBackend::join_codegen_and_link"); + } +} + +pub struct DummyMetadataLoader(()); + +impl MetadataLoader for DummyMetadataLoader { + fn get_rlib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result<MetadataRef, String> { + bug!("DummyMetadataLoader::get_rlib_metadata"); + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result<MetadataRef, String> { + bug!("DummyMetadataLoader::get_dylib_metadata"); + } +} + +pub struct NoLlvmMetadataLoader; + +impl MetadataLoader for NoLlvmMetadataLoader { + fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result<MetadataRef, String> { + let file = File::open(filename) + .map_err(|e| format!("metadata file open err: {:?}", e))?; + let mut archive = Archive::new(file); + + while let Some(entry_result) = archive.next_entry() { + let mut entry = entry_result + .map_err(|e| format!("metadata section read err: {:?}", e))?; + if entry.header().identifier() == "rust.metadata.bin" { + let mut buf = Vec::new(); + io::copy(&mut entry, &mut buf).unwrap(); + let buf: OwningRef<Vec<u8>, [u8]> = OwningRef::new(buf).into(); + return Ok(rustc_erase_owner!(buf.map_owner_box())); + } + } + + Err("Couldn't find metadata section".to_string()) + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path, + ) -> Result<MetadataRef, String> { + // FIXME: Support reading dylibs from llvm enabled rustc + self.get_rlib_metadata(_target, _filename) + } +} + +pub struct MetadataOnlyCodegenBackend(()); +pub struct OngoingCodegen { + metadata: EncodedMetadata, + metadata_version: Vec<u8>, + crate_name: Symbol, +} + +impl MetadataOnlyCodegenBackend { + pub fn new() -> Box<CodegenBackend> { + box MetadataOnlyCodegenBackend(()) + } +} + +impl CodegenBackend for MetadataOnlyCodegenBackend { + fn init(&self, sess: &Session) { + for cty in sess.opts.crate_types.iter() { + match *cty { + CrateType::CrateTypeRlib | CrateType::CrateTypeDylib | + CrateType::CrateTypeExecutable => {}, + _ => { + sess.parse_sess.span_diagnostic.warn( + &format!("LLVM unsupported, so output type {} is not supported", cty) + ); + }, + } + } + } + + fn metadata_loader(&self) -> Box<MetadataLoader + Sync> { + box NoLlvmMetadataLoader + } + + fn provide(&self, providers: &mut Providers) { + ::symbol_names::provide(providers); + + providers.target_features_whitelist = |_tcx, _cnum| { + Lrc::new(FxHashMap()) // Just a dummy + }; + } + fn provide_extern(&self, _providers: &mut Providers) {} + + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver<Box<Any + Send>> + ) -> Box<Any> { + use rustc_mir::monomorphize::item::MonoItem; + + ::check_for_rustc_errors_attr(tcx); + ::symbol_names_test::report_symbol_names(tcx); + ::rustc_incremental::assert_dep_graph(tcx); + ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); + ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, + collector::collect_crate_mono_items( + tcx, + collector::MonoItemCollectionMode::Eager + ).0.iter() + ); + ::rustc::middle::dependency_format::calculate(tcx); + let _ = tcx.link_args(LOCAL_CRATE); + let _ = tcx.native_libraries(LOCAL_CRATE); + for mono_item in + collector::collect_crate_mono_items( + tcx, + collector::MonoItemCollectionMode::Eager + ).0 { + match mono_item { + MonoItem::Fn(inst) => { + let def_id = inst.def_id(); + if def_id.is_local() { + let _ = inst.def.is_inline(tcx); + let _ = tcx.codegen_fn_attrs(def_id); + } + } + _ => {} + } + } + tcx.sess.abort_if_errors(); + + let link_meta = build_link_meta(tcx.crate_hash(LOCAL_CRATE)); + let metadata = tcx.encode_metadata(&link_meta); + + box OngoingCodegen { + metadata: metadata, + metadata_version: tcx.metadata_encoding_version().to_vec(), + crate_name: tcx.crate_name(LOCAL_CRATE), + } + } + + fn join_codegen_and_link( + &self, + ongoing_codegen: Box<Any>, + sess: &Session, + _dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete> { + let ongoing_codegen = ongoing_codegen.downcast::<OngoingCodegen>() + .expect("Expected MetadataOnlyCodegenBackend's OngoingCodegen, found Box<Any>"); + for &crate_type in sess.opts.crate_types.iter() { + if crate_type != CrateType::CrateTypeRlib && crate_type != CrateType::CrateTypeDylib { + continue; + } + let output_name = + out_filename(sess, crate_type, &outputs, &ongoing_codegen.crate_name.as_str()); + let mut compressed = ongoing_codegen.metadata_version.clone(); + let metadata = if crate_type == CrateType::CrateTypeDylib { + DeflateEncoder::new(&mut compressed, Compression::fast()) + .write_all(&ongoing_codegen.metadata.raw_data) + .unwrap(); + &compressed + } else { + &ongoing_codegen.metadata.raw_data + }; + let mut builder = Builder::new(File::create(&output_name).unwrap()); + let header = Header::new("rust.metadata.bin".to_string(), metadata.len() as u64); + builder.append(&header, Cursor::new(metadata)).unwrap(); + } + + sess.abort_if_errors(); + if !sess.opts.crate_types.contains(&CrateType::CrateTypeRlib) + && !sess.opts.crate_types.contains(&CrateType::CrateTypeDylib) + { + sess.fatal("Executables are not supported by the metadata-only backend."); + } + Ok(()) + } +} diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs new file mode 100644 index 00000000000..0c18571f4ff --- /dev/null +++ b/src/librustc_codegen_utils/lib.rs @@ -0,0 +1,63 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(custom_attribute)] +#![allow(unused_attributes)] +#![feature(quote)] +#![feature(rustc_diagnostic_macros)] + +extern crate ar; +extern crate flate2; +#[macro_use] +extern crate log; + +#[macro_use] +extern crate rustc; +extern crate rustc_target; +extern crate rustc_mir; +extern crate rustc_incremental; +extern crate syntax; +extern crate syntax_pos; +#[macro_use] extern crate rustc_data_structures; + +pub extern crate rustc as __rustc; + +use rustc::ty::TyCtxt; + +pub mod link; +pub mod codegen_backend; +pub mod symbol_names; +pub mod symbol_names_test; + +/// check for the #[rustc_error] annotation, which forces an +/// error in codegen. This is used to write compile-fail tests +/// that actually test that compilation succeeds without +/// reporting an error. +pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { + if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() { + let main_def_id = tcx.hir.local_def_id(id); + + if tcx.has_attr(main_def_id, "rustc_error") { + tcx.sess.span_fatal(span, "compilation successful"); + } + } +} + +__build_diagnostic_array! { librustc_codegen_utils, DIAGNOSTICS } diff --git a/src/librustc_codegen_utils/link.rs b/src/librustc_codegen_utils/link.rs new file mode 100644 index 00000000000..aabe931d79c --- /dev/null +++ b/src/librustc_codegen_utils/link.rs @@ -0,0 +1,191 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::session::config::{self, OutputFilenames, Input, OutputType}; +use rustc::session::Session; +use rustc::middle::cstore::{self, LinkMeta}; +use rustc::hir::svh::Svh; +use std::path::{Path, PathBuf}; +use syntax::{ast, attr}; +use syntax_pos::Span; + +pub fn out_filename(sess: &Session, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) + -> PathBuf { + let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); + let out_filename = outputs.outputs.get(&OutputType::Exe) + .and_then(|s| s.to_owned()) + .or_else(|| outputs.single_output_file.clone()) + .unwrap_or(default_filename); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers +// check this already -- however, the Linux linker will happily overwrite a +// read-only file. We should be consistent. +pub fn check_file_is_writeable(file: &Path, sess: &Session) { + if !is_writeable(file) { + sess.fatal(&format!("output file {} is not writeable -- check its \ + permissions", file.display())); + } +} + +fn is_writeable(p: &Path) -> bool { + match p.metadata() { + Err(..) => true, + Ok(m) => !m.permissions().readonly() + } +} + +pub fn build_link_meta(crate_hash: Svh) -> LinkMeta { + let r = LinkMeta { + crate_hash, + }; + info!("{:?}", r); + return r; +} + +pub fn find_crate_name(sess: Option<&Session>, + attrs: &[ast::Attribute], + input: &Input) -> String { + let validate = |s: String, span: Option<Span>| { + cstore::validate_crate_name(sess, &s, span); + s + }; + + // Look in attributes 100% of the time to make sure the attribute is marked + // as used. After doing this, however, we still prioritize a crate name from + // the command line over one found in the #[crate_name] attribute. If we + // find both we ensure that they're the same later on as well. + let attr_crate_name = attr::find_by_name(attrs, "crate_name") + .and_then(|at| at.value_str().map(|s| (at, s))); + + if let Some(sess) = sess { + if let Some(ref s) = sess.opts.crate_name { + if let Some((attr, name)) = attr_crate_name { + if name != &**s { + let msg = format!("--crate-name and #[crate_name] are \ + required to match, but `{}` != `{}`", + s, name); + sess.span_err(attr.span, &msg); + } + } + return validate(s.clone(), None); + } + } + + if let Some((attr, s)) = attr_crate_name { + return validate(s.to_string(), Some(attr.span)); + } + if let Input::File(ref path) = *input { + if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { + if s.starts_with("-") { + let msg = format!("crate names cannot start with a `-`, but \ + `{}` has a leading hyphen", s); + if let Some(sess) = sess { + sess.err(&msg); + } + } else { + return validate(s.replace("-", "_"), None); + } + } + } + + "rust_out".to_string() +} + +pub fn filename_for_input(sess: &Session, + crate_type: config::CrateType, + crate_name: &str, + outputs: &OutputFilenames) -> PathBuf { + let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); + + match crate_type { + config::CrateTypeRlib => { + outputs.out_directory.join(&format!("lib{}.rlib", libname)) + } + config::CrateTypeCdylib | + config::CrateTypeProcMacro | + config::CrateTypeDylib => { + let (prefix, suffix) = (&sess.target.target.options.dll_prefix, + &sess.target.target.options.dll_suffix); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, + suffix)) + } + config::CrateTypeStaticlib => { + let (prefix, suffix) = (&sess.target.target.options.staticlib_prefix, + &sess.target.target.options.staticlib_suffix); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, + suffix)) + } + config::CrateTypeExecutable => { + let suffix = &sess.target.target.options.exe_suffix; + let out_filename = outputs.path(OutputType::Exe); + if suffix.is_empty() { + out_filename.to_path_buf() + } else { + out_filename.with_extension(&suffix[1..]) + } + } + } +} + +/// Returns default crate type for target +/// +/// Default crate type is used when crate type isn't provided neither +/// through cmd line arguments nor through crate attributes +/// +/// It is CrateTypeExecutable for all platforms but iOS as there is no +/// way to run iOS binaries anyway without jailbreaking and +/// interaction with Rust code through static library is the only +/// option for now +pub fn default_output_for_target(sess: &Session) -> config::CrateType { + if !sess.target.target.options.executables { + config::CrateTypeStaticlib + } else { + config::CrateTypeExecutable + } +} + +/// Checks if target supports crate_type as output +pub fn invalid_output_for_target(sess: &Session, + crate_type: config::CrateType) -> bool { + match crate_type { + config::CrateTypeCdylib | + config::CrateTypeDylib | + config::CrateTypeProcMacro => { + if !sess.target.target.options.dynamic_linking { + return true + } + if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs { + return true + } + } + _ => {} + } + if sess.target.target.options.only_cdylib { + match crate_type { + config::CrateTypeProcMacro | config::CrateTypeDylib => return true, + _ => {} + } + } + if !sess.target.target.options.executables { + if crate_type == config::CrateTypeExecutable { + return true + } + } + + false +} diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs new file mode 100644 index 00000000000..1a62f39ae3d --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -0,0 +1,435 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust Linkage Model and Symbol Names +//! ======================================= +//! +//! The semantic model of Rust linkage is, broadly, that "there's no global +//! namespace" between crates. Our aim is to preserve the illusion of this +//! model despite the fact that it's not *quite* possible to implement on +//! modern linkers. We initially didn't use system linkers at all, but have +//! been convinced of their utility. +//! +//! There are a few issues to handle: +//! +//! - Linkers operate on a flat namespace, so we have to flatten names. +//! We do this using the C++ namespace-mangling technique. Foo::bar +//! symbols and such. +//! +//! - Symbols for distinct items with the same *name* need to get different +//! linkage-names. Examples of this are monomorphizations of functions or +//! items within anonymous scopes that end up having the same path. +//! +//! - Symbols in different crates but with same names "within" the crate need +//! to get different linkage-names. +//! +//! - Symbol names should be deterministic: Two consecutive runs of the +//! compiler over the same code base should produce the same symbol names for +//! the same items. +//! +//! - Symbol names should not depend on any global properties of the code base, +//! so that small modifications to the code base do not result in all symbols +//! changing. In previous versions of the compiler, symbol names incorporated +//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be +//! infeasible when used in conjunction with incremental compilation because +//! small code changes would invalidate all symbols generated previously. +//! +//! - Even symbols from different versions of the same crate should be able to +//! live next to each other without conflict. +//! +//! In order to fulfill the above requirements the following scheme is used by +//! the compiler: +//! +//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit +//! hash value into every exported symbol name. Anything that makes a difference +//! to the symbol being named, but does not show up in the regular path needs to +//! be fed into this hash: +//! +//! - Different monomorphizations of the same item have the same path but differ +//! in their concrete type parameters, so these parameters are part of the +//! data being digested for the symbol hash. +//! +//! - Rust allows items to be defined in anonymous scopes, such as in +//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have +//! the path `foo::bar`, since the anonymous scopes do not contribute to the +//! path of an item. The compiler already handles this case via so-called +//! disambiguating `DefPaths` which use indices to distinguish items with the +//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` +//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation +//! information into the symbol name too, these indices are fed into the +//! symbol hash, so that the above two symbols would end up with different +//! hash values. +//! +//! The two measures described above suffice to avoid intra-crate conflicts. In +//! order to also avoid inter-crate conflicts two more measures are taken: +//! +//! - The name of the crate containing the symbol is prepended to the symbol +//! name, i.e. symbols are "crate qualified". For example, a function `foo` in +//! module `bar` in crate `baz` would get a symbol name like +//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids +//! simple conflicts between functions from different crates. +//! +//! - In order to be able to also use symbols from two versions of the same +//! crate (which naturally also have the same name), a stronger measure is +//! required: The compiler accepts an arbitrary "disambiguator" value via the +//! `-C metadata` commandline argument. This disambiguator is then fed into +//! the symbol hash of every exported item. Consequently, the symbols in two +//! identical crates but with different disambiguators are not in conflict +//! with each other. This facility is mainly intended to be used by build +//! tools like Cargo. +//! +//! A note on symbol name stability +//! ------------------------------- +//! Previous versions of the compiler resorted to feeding NodeIds into the +//! symbol hash in order to disambiguate between items with the same path. The +//! current version of the name generation algorithm takes great care not to do +//! that, since NodeIds are notoriously unstable: A small change to the +//! code base will offset all NodeIds after the change and thus, much as using +//! the SVH in the hash, invalidate an unbounded number of symbol names. This +//! makes re-using previously compiled code for incremental compilation +//! virtually impossible. Thus, symbol hash generation exclusively relies on +//! DefPaths which are much more robust in the face of changes to the code base. + +use rustc::middle::weak_lang_items; +use rustc_mir::monomorphize::Instance; +use rustc_mir::monomorphize::item::{MonoItem, MonoItemExt, InstantiationMode}; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::map as hir_map; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::fold::TypeVisitor; +use rustc::ty::item_path::{self, ItemPathBuffer, RootMode}; +use rustc::ty::maps::Providers; +use rustc::ty::subst::Substs; +use rustc::hir::map::definitions::DefPathData; +use rustc::util::common::record_time; + +use syntax::attr; +use syntax_pos::symbol::Symbol; + +use std::fmt::Write; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + def_symbol_name, + symbol_name, + + ..*providers + }; +} + +fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + + // the DefId of the item this name is for + def_id: DefId, + + // instance this name will be for + instance: Instance<'tcx>, + + // type of the item, without any generic + // parameters substituted; this is + // included in the hash as a kind of + // safeguard. + item_type: Ty<'tcx>, + + // values for generic type parameters, + // if any. + substs: &'tcx Substs<'tcx>) + -> u64 { + debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); + + let mut hasher = ty::util::TypeIdHasher::<u64>::new(tcx); + + record_time(&tcx.sess.perf_stats.symbol_hash_time, || { + // the main symbol name is not necessarily unique; hash in the + // compiler's internal def-path, guaranteeing each symbol has a + // truly unique path + hasher.hash(tcx.def_path_hash(def_id)); + + // Include the main item-type. Note that, in this case, the + // assertions about `needs_subst` may not hold, but this item-type + // ought to be the same for every reference anyway. + assert!(!item_type.has_erasable_regions()); + hasher.visit_ty(item_type); + + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::TyFnDef(..) = item_type.sty { + item_type.fn_sig(tcx).visit_with(&mut hasher); + } + + // also include any type parameters (for generic items) + assert!(!substs.has_erasable_regions()); + assert!(!substs.needs_subst()); + substs.visit_with(&mut hasher); + + let is_generic = substs.types().next().is_some(); + let avoid_cross_crate_conflicts = + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + is_generic || + + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compliation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + match MonoItem::Fn(instance).instantiation_mode(tcx) { + InstantiationMode::GloballyShared { may_conflict: true } => true, + _ => false, + }; + + if avoid_cross_crate_conflicts { + let instantiating_crate = if is_generic { + if !def_id.is_local() && tcx.share_generics() { + // If we are re-using a monomorphization from another crate, + // we have to compute the symbol hash accordingly. + let upstream_monomorphizations = + tcx.upstream_monomorphizations_for(def_id); + + upstream_monomorphizations.and_then(|monos| monos.get(&substs) + .cloned()) + .unwrap_or(LOCAL_CRATE) + } else { + LOCAL_CRATE + } + } else { + LOCAL_CRATE + }; + + hasher.hash(&tcx.original_crate_name(instantiating_crate).as_str()[..]); + hasher.hash(&tcx.crate_disambiguator(instantiating_crate)); + } + }); + + // 64 bits should be enough to avoid collisions. + hasher.finish() +} + +fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) + -> ty::SymbolName +{ + let mut buffer = SymbolPathBuffer::new(); + item_path::with_forced_absolute_paths(|| { + tcx.push_item_path(&mut buffer, def_id); + }); + buffer.into_interned() +} + +fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) + -> ty::SymbolName +{ + ty::SymbolName { name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str() } +} + +fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) + -> String +{ + let def_id = instance.def_id(); + let substs = instance.substs; + + debug!("symbol_name(def_id={:?}, substs={:?})", + def_id, substs); + + let node_id = tcx.hir.as_local_node_id(def_id); + + if let Some(id) = node_id { + if *tcx.sess.plugin_registrar_fn.get() == Some(id) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_plugin_registrar_symbol(disambiguator); + } + if *tcx.sess.derive_registrar_fn.get() == Some(id) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_derive_registrar_symbol(disambiguator); + } + } + + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let attrs = tcx.get_attrs(def_id); + let is_foreign = if let Some(id) = node_id { + match tcx.hir.get(id) { + hir_map::NodeForeignItem(_) => true, + _ => false + } + } else { + tcx.is_foreign_item(def_id) + }; + + if let Some(name) = weak_lang_items::link_name(&attrs) { + return name.to_string(); + } + + if is_foreign { + if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { + return name.to_string(); + } + // Don't mangle foreign items. + return tcx.item_name(def_id).to_string(); + } + + if let Some(name) = tcx.codegen_fn_attrs(def_id).export_name { + // Use provided name + return name.to_string(); + } + + if attr::contains_name(&attrs, "no_mangle") { + // Don't mangle + return tcx.item_name(def_id).to_string(); + } + + // We want to compute the "type" of this item. Unfortunately, some + // kinds of items (e.g., closures) don't have an entry in the + // item-type array. So walk back up the find the closest parent + // that DOES have an entry. + let mut ty_def_id = def_id; + let instance_ty; + loop { + let key = tcx.def_key(ty_def_id); + match key.disambiguated_data.data { + DefPathData::TypeNs(_) | + DefPathData::ValueNs(_) => { + instance_ty = tcx.type_of(ty_def_id); + break; + } + _ => { + // if we're making a symbol for something, there ought + // to be a value or type-def or something in there + // *somewhere* + ty_def_id.index = key.parent.unwrap_or_else(|| { + bug!("finding type for {:?}, encountered def-id {:?} with no \ + parent", def_id, ty_def_id); + }); + } + } + } + + // Erase regions because they may not be deterministic when hashed + // and should not matter anyhow. + let instance_ty = tcx.erase_regions(&instance_ty); + + let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs); + + SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash) +} + +// Follow C++ namespace-mangling style, see +// http://en.wikipedia.org/wiki/Name_mangling for more info. +// +// It turns out that on macOS you can actually have arbitrary symbols in +// function names (at least when given to LLVM), but this is not possible +// when using unix's linker. Perhaps one day when we just use a linker from LLVM +// we won't need to do this name mangling. The problem with name mangling is +// that it seriously limits the available characters. For example we can't +// have things like &T in symbol names when one would theoretically +// want them for things like impls of traits on that type. +// +// To be able to work on all platforms and get *some* reasonable output, we +// use C++ name-mangling. +struct SymbolPathBuffer { + result: String, + temp_buf: String +} + +impl SymbolPathBuffer { + fn new() -> Self { + let mut result = SymbolPathBuffer { + result: String::with_capacity(64), + temp_buf: String::with_capacity(16) + }; + result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested + result + } + + fn from_interned(symbol: ty::SymbolName) -> Self { + let mut result = SymbolPathBuffer { + result: String::with_capacity(64), + temp_buf: String::with_capacity(16) + }; + result.result.push_str(&symbol.name.as_str()); + result + } + + fn into_interned(self) -> ty::SymbolName { + ty::SymbolName { name: Symbol::intern(&self.result).as_interned_str() } + } + + fn finish(mut self, hash: u64) -> String { + // E = end name-sequence + let _ = write!(self.result, "17h{:016x}E", hash); + self.result + } +} + +impl ItemPathBuffer for SymbolPathBuffer { + fn root_mode(&self) -> &RootMode { + const ABSOLUTE: &'static RootMode = &RootMode::Absolute; + ABSOLUTE + } + + fn push(&mut self, text: &str) { + self.temp_buf.clear(); + let need_underscore = sanitize(&mut self.temp_buf, text); + let _ = write!(self.result, "{}", self.temp_buf.len() + (need_underscore as usize)); + if need_underscore { + self.result.push('_'); + } + self.result.push_str(&self.temp_buf); + } +} + +// Name sanitation. LLVM will happily accept identifiers with weird names, but +// gas doesn't! +// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ +// +// returns true if an underscore must be added at the start +pub fn sanitize(result: &mut String, s: &str) -> bool { + for c in s.chars() { + match c { + // Escape these with $ sequences + '@' => result.push_str("$SP$"), + '*' => result.push_str("$BP$"), + '&' => result.push_str("$RF$"), + '<' => result.push_str("$LT$"), + '>' => result.push_str("$GT$"), + '(' => result.push_str("$LP$"), + ')' => result.push_str("$RP$"), + ',' => result.push_str("$C$"), + + // '.' doesn't occur in types and functions, so reuse it + // for ':' and '-' + '-' | ':' => result.push('.'), + + // These are legal symbols + 'a' ... 'z' + | 'A' ... 'Z' + | '0' ... '9' + | '_' | '.' | '$' => result.push(c), + + _ => { + result.push('$'); + for c in c.escape_unicode().skip(1) { + match c { + '{' => {}, + '}' => result.push('$'), + c => result.push(c), + } + } + } + } + } + + // Underscore-qualify anything that didn't start as an ident. + !result.is_empty() && + result.as_bytes()[0] != '_' as u8 && + ! (result.as_bytes()[0] as char).is_xid_start() +} diff --git a/src/librustc_codegen_utils/symbol_names_test.rs b/src/librustc_codegen_utils/symbol_names_test.rs new file mode 100644 index 00000000000..47bbd67fb5c --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names_test.rs @@ -0,0 +1,79 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_item_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! item-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use rustc::hir; +use rustc::ty::TyCtxt; +use syntax::ast; + +use rustc_mir::monomorphize::Instance; + +const SYMBOL_NAME: &'static str = "rustc_symbol_name"; +const ITEM_PATH: &'static str = "rustc_item_path"; + +pub fn report_symbol_names<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + // if the `rustc_attrs` feature is not enabled, then the + // attributes we are interested in cannot be present anyway, so + // skip the walk. + if !tcx.features().rustc_attrs { + return; + } + + tcx.dep_graph.with_ignore(|| { + let mut visitor = SymbolNamesTest { tcx: tcx }; + tcx.hir.krate().visit_all_item_likes(&mut visitor); + }) +} + +struct SymbolNamesTest<'a, 'tcx:'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { + fn process_attrs(&mut self, + node_id: ast::NodeId) { + let tcx = self.tcx; + let def_id = tcx.hir.local_def_id(node_id); + for attr in tcx.get_attrs(def_id).iter() { + if attr.check_name(SYMBOL_NAME) { + // for now, can only use on monomorphic names + let instance = Instance::mono(tcx, def_id); + let name = self.tcx.symbol_name(instance); + tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); + } else if attr.check_name(ITEM_PATH) { + let path = tcx.item_path_str(def_id); + tcx.sess.span_err(attr.span, &format!("item-path({})", path)); + } + + // (*) The formatting of `tag({})` is chosen so that tests can elect + // to test the entirety of the string, if they choose, or else just + // some subset. + } + } +} + +impl<'a, 'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + self.process_attrs(item.id); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { + self.process_attrs(trait_item.id); + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { + self.process_attrs(impl_item.id); + } +} |
