summary refs log tree commit diff
path: root/src/librustc_codegen_utils
diff options
context:
space:
mode:
authorIrina Popa <irinagpopa@gmail.com>2018-05-08 16:10:16 +0300
committerIrina Popa <irinagpopa@gmail.com>2018-05-17 15:08:30 +0300
commitb63d7e2b1c4019e40051036bcb1fd5f254a8f6e2 (patch)
tree314792e2f467d17181d29d4988550058197ac029 /src/librustc_codegen_utils
parente3150564f889a3bad01795d9fcb31d4f14d58a99 (diff)
downloadrust-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.toml23
-rw-r--r--src/librustc_codegen_utils/codegen_backend.rs296
-rw-r--r--src/librustc_codegen_utils/lib.rs63
-rw-r--r--src/librustc_codegen_utils/link.rs191
-rw-r--r--src/librustc_codegen_utils/symbol_names.rs435
-rw-r--r--src/librustc_codegen_utils/symbol_names_test.rs79
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);
+    }
+}