mv compiler to compiler/
This commit is contained in:
parent
db534b3ac2
commit
9e5f7d5631
1686 changed files with 941 additions and 1051 deletions
56
compiler/rustc_codegen_ssa/src/back/archive.rs
Normal file
56
compiler/rustc_codegen_ssa/src/back/archive.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub fn find_library(name: Symbol, search_paths: &[PathBuf], sess: &Session) -> PathBuf {
|
||||
// On Windows, static libraries sometimes show up as libfoo.a and other
|
||||
// times show up as foo.lib
|
||||
let oslibname = format!(
|
||||
"{}{}{}",
|
||||
sess.target.target.options.staticlib_prefix,
|
||||
name,
|
||||
sess.target.target.options.staticlib_suffix
|
||||
);
|
||||
let unixlibname = format!("lib{}.a", name);
|
||||
|
||||
for path in search_paths {
|
||||
debug!("looking for {} inside {:?}", name, path);
|
||||
let test = path.join(&oslibname);
|
||||
if test.exists() {
|
||||
return test;
|
||||
}
|
||||
if oslibname != unixlibname {
|
||||
let test = path.join(&unixlibname);
|
||||
if test.exists() {
|
||||
return test;
|
||||
}
|
||||
}
|
||||
}
|
||||
sess.fatal(&format!(
|
||||
"could not find native static library `{}`, \
|
||||
perhaps an -L flag is missing?",
|
||||
name
|
||||
));
|
||||
}
|
||||
|
||||
pub trait ArchiveBuilder<'a> {
|
||||
fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self;
|
||||
|
||||
fn add_file(&mut self, path: &Path);
|
||||
fn remove_file(&mut self, name: &str);
|
||||
fn src_files(&mut self) -> Vec<String>;
|
||||
|
||||
fn add_rlib(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
name: &str,
|
||||
lto: bool,
|
||||
skip_objects: bool,
|
||||
) -> io::Result<()>;
|
||||
fn add_native_library(&mut self, name: Symbol);
|
||||
fn update_symbols(&mut self);
|
||||
|
||||
fn build(self);
|
||||
}
|
183
compiler/rustc_codegen_ssa/src/back/command.rs
Normal file
183
compiler/rustc_codegen_ssa/src/back/command.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
//! A thin wrapper around `Command` in the standard library which allows us to
|
||||
//! read the arguments that are built up.
|
||||
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::process::{self, Output};
|
||||
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_target::spec::LldFlavor;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Command {
|
||||
program: Program,
|
||||
args: Vec<OsString>,
|
||||
env: Vec<(OsString, OsString)>,
|
||||
env_remove: Vec<OsString>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Program {
|
||||
Normal(OsString),
|
||||
CmdBatScript(OsString),
|
||||
Lld(OsString, LldFlavor),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn new<P: AsRef<OsStr>>(program: P) -> Command {
|
||||
Command::_new(Program::Normal(program.as_ref().to_owned()))
|
||||
}
|
||||
|
||||
pub fn bat_script<P: AsRef<OsStr>>(program: P) -> Command {
|
||||
Command::_new(Program::CmdBatScript(program.as_ref().to_owned()))
|
||||
}
|
||||
|
||||
pub fn lld<P: AsRef<OsStr>>(program: P, flavor: LldFlavor) -> Command {
|
||||
Command::_new(Program::Lld(program.as_ref().to_owned(), flavor))
|
||||
}
|
||||
|
||||
fn _new(program: Program) -> Command {
|
||||
Command { program, args: Vec::new(), env: Vec::new(), env_remove: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn arg<P: AsRef<OsStr>>(&mut self, arg: P) -> &mut Command {
|
||||
self._arg(arg.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sym_arg(&mut self, arg: Symbol) -> &mut Command {
|
||||
self.arg(&*arg.as_str());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn args<I>(&mut self, args: I) -> &mut Command
|
||||
where
|
||||
I: IntoIterator<Item: AsRef<OsStr>>,
|
||||
{
|
||||
for arg in args {
|
||||
self._arg(arg.as_ref());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn _arg(&mut self, arg: &OsStr) {
|
||||
self.args.push(arg.to_owned());
|
||||
}
|
||||
|
||||
pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Command
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
V: AsRef<OsStr>,
|
||||
{
|
||||
self._env(key.as_ref(), value.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
fn _env(&mut self, key: &OsStr, value: &OsStr) {
|
||||
self.env.push((key.to_owned(), value.to_owned()));
|
||||
}
|
||||
|
||||
pub fn env_remove<K>(&mut self, key: K) -> &mut Command
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
{
|
||||
self._env_remove(key.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
fn _env_remove(&mut self, key: &OsStr) {
|
||||
self.env_remove.push(key.to_owned());
|
||||
}
|
||||
|
||||
pub fn output(&mut self) -> io::Result<Output> {
|
||||
self.command().output()
|
||||
}
|
||||
|
||||
pub fn command(&self) -> process::Command {
|
||||
let mut ret = match self.program {
|
||||
Program::Normal(ref p) => process::Command::new(p),
|
||||
Program::CmdBatScript(ref p) => {
|
||||
let mut c = process::Command::new("cmd");
|
||||
c.arg("/c").arg(p);
|
||||
c
|
||||
}
|
||||
Program::Lld(ref p, flavor) => {
|
||||
let mut c = process::Command::new(p);
|
||||
c.arg("-flavor").arg(match flavor {
|
||||
LldFlavor::Wasm => "wasm",
|
||||
LldFlavor::Ld => "gnu",
|
||||
LldFlavor::Link => "link",
|
||||
LldFlavor::Ld64 => "darwin",
|
||||
});
|
||||
c
|
||||
}
|
||||
};
|
||||
ret.args(&self.args);
|
||||
ret.envs(self.env.clone());
|
||||
for k in &self.env_remove {
|
||||
ret.env_remove(k);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
// extensions
|
||||
|
||||
pub fn get_args(&self) -> &[OsString] {
|
||||
&self.args
|
||||
}
|
||||
|
||||
pub fn take_args(&mut self) -> Vec<OsString> {
|
||||
mem::take(&mut self.args)
|
||||
}
|
||||
|
||||
/// Returns a `true` if we're pretty sure that this'll blow OS spawn limits,
|
||||
/// or `false` if we should attempt to spawn and see what the OS says.
|
||||
pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool {
|
||||
// We mostly only care about Windows in this method, on Unix the limits
|
||||
// can be gargantuan anyway so we're pretty unlikely to hit them
|
||||
if cfg!(unix) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Right now LLD doesn't support the `@` syntax of passing an argument
|
||||
// through files, so regardless of the platform we try to go to the OS
|
||||
// on this one.
|
||||
if let Program::Lld(..) = self.program {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ok so on Windows to spawn a process is 32,768 characters in its
|
||||
// command line [1]. Unfortunately we don't actually have access to that
|
||||
// as it's calculated just before spawning. Instead we perform a
|
||||
// poor-man's guess as to how long our command line will be. We're
|
||||
// assuming here that we don't have to escape every character...
|
||||
//
|
||||
// Turns out though that `cmd.exe` has even smaller limits, 8192
|
||||
// characters [2]. Linkers can often be batch scripts (for example
|
||||
// Emscripten, Gecko's current build system) which means that we're
|
||||
// running through batch scripts. These linkers often just forward
|
||||
// arguments elsewhere (and maybe tack on more), so if we blow 8192
|
||||
// bytes we'll typically cause them to blow as well.
|
||||
//
|
||||
// Basically as a result just perform an inflated estimate of what our
|
||||
// command line will look like and test if it's > 8192 (we actually
|
||||
// test against 6k to artificially inflate our estimate). If all else
|
||||
// fails we'll fall back to the normal unix logic of testing the OS
|
||||
// error code if we fail to spawn and automatically re-spawning the
|
||||
// linker with smaller arguments.
|
||||
//
|
||||
// [1]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
|
||||
// [2]: https://devblogs.microsoft.com/oldnewthing/?p=41553
|
||||
|
||||
let estimated_command_line_len = self.args.iter().map(|a| a.len()).sum::<usize>();
|
||||
estimated_command_line_len > 1024 * 6
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Command {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.command().fmt(f)
|
||||
}
|
||||
}
|
2152
compiler/rustc_codegen_ssa/src/back/link.rs
Normal file
2152
compiler/rustc_codegen_ssa/src/back/link.rs
Normal file
File diff suppressed because it is too large
Load diff
1351
compiler/rustc_codegen_ssa/src/back/linker.rs
Normal file
1351
compiler/rustc_codegen_ssa/src/back/linker.rs
Normal file
File diff suppressed because it is too large
Load diff
107
compiler/rustc_codegen_ssa/src/back/lto.rs
Normal file
107
compiler/rustc_codegen_ssa/src/back/lto.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use super::write::CodegenContext;
|
||||
use crate::traits::*;
|
||||
use crate::ModuleCodegen;
|
||||
|
||||
use rustc_errors::FatalError;
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct ThinModule<B: WriteBackendMethods> {
|
||||
pub shared: Arc<ThinShared<B>>,
|
||||
pub idx: usize,
|
||||
}
|
||||
|
||||
impl<B: WriteBackendMethods> ThinModule<B> {
|
||||
pub fn name(&self) -> &str {
|
||||
self.shared.module_names[self.idx].to_str().unwrap()
|
||||
}
|
||||
|
||||
pub fn cost(&self) -> u64 {
|
||||
// Yes, that's correct, we're using the size of the bytecode as an
|
||||
// indicator for how costly this codegen unit is.
|
||||
self.data().len() as u64
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &[u8] {
|
||||
let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data());
|
||||
a.unwrap_or_else(|| {
|
||||
let len = self.shared.thin_buffers.len();
|
||||
self.shared.serialized_modules[self.idx - len].data()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThinShared<B: WriteBackendMethods> {
|
||||
pub data: B::ThinData,
|
||||
pub thin_buffers: Vec<B::ThinBuffer>,
|
||||
pub serialized_modules: Vec<SerializedModule<B::ModuleBuffer>>,
|
||||
pub module_names: Vec<CString>,
|
||||
}
|
||||
|
||||
pub enum LtoModuleCodegen<B: WriteBackendMethods> {
|
||||
Fat {
|
||||
module: Option<ModuleCodegen<B::Module>>,
|
||||
_serialized_bitcode: Vec<SerializedModule<B::ModuleBuffer>>,
|
||||
},
|
||||
|
||||
Thin(ThinModule<B>),
|
||||
}
|
||||
|
||||
impl<B: WriteBackendMethods> LtoModuleCodegen<B> {
|
||||
pub fn name(&self) -> &str {
|
||||
match *self {
|
||||
LtoModuleCodegen::Fat { .. } => "everything",
|
||||
LtoModuleCodegen::Thin(ref m) => m.name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimize this module within the given codegen context.
|
||||
///
|
||||
/// This function is unsafe as it'll return a `ModuleCodegen` still
|
||||
/// points to LLVM data structures owned by this `LtoModuleCodegen`.
|
||||
/// It's intended that the module returned is immediately code generated and
|
||||
/// dropped, and then this LTO module is dropped.
|
||||
pub unsafe fn optimize(
|
||||
&mut self,
|
||||
cgcx: &CodegenContext<B>,
|
||||
) -> Result<ModuleCodegen<B::Module>, FatalError> {
|
||||
match *self {
|
||||
LtoModuleCodegen::Fat { ref mut module, .. } => {
|
||||
let module = module.take().unwrap();
|
||||
{
|
||||
let config = cgcx.config(module.kind);
|
||||
B::run_lto_pass_manager(cgcx, &module, config, false);
|
||||
}
|
||||
Ok(module)
|
||||
}
|
||||
LtoModuleCodegen::Thin(ref mut thin) => B::optimize_thin(cgcx, thin),
|
||||
}
|
||||
}
|
||||
|
||||
/// A "gauge" of how costly it is to optimize this module, used to sort
|
||||
/// biggest modules first.
|
||||
pub fn cost(&self) -> u64 {
|
||||
match *self {
|
||||
// Only one module with fat LTO, so the cost doesn't matter.
|
||||
LtoModuleCodegen::Fat { .. } => 0,
|
||||
LtoModuleCodegen::Thin(ref m) => m.cost(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SerializedModule<M: ModuleBufferMethods> {
|
||||
Local(M),
|
||||
FromRlib(Vec<u8>),
|
||||
FromUncompressedFile(memmap::Mmap),
|
||||
}
|
||||
|
||||
impl<M: ModuleBufferMethods> SerializedModule<M> {
|
||||
pub fn data(&self) -> &[u8] {
|
||||
match *self {
|
||||
SerializedModule::Local(ref m) => m.data(),
|
||||
SerializedModule::FromRlib(ref m) => m,
|
||||
SerializedModule::FromUncompressedFile(ref m) => m,
|
||||
}
|
||||
}
|
||||
}
|
8
compiler/rustc_codegen_ssa/src/back/mod.rs
Normal file
8
compiler/rustc_codegen_ssa/src/back/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
pub mod archive;
|
||||
pub mod command;
|
||||
pub mod link;
|
||||
pub mod linker;
|
||||
pub mod lto;
|
||||
pub mod rpath;
|
||||
pub mod symbol_export;
|
||||
pub mod write;
|
135
compiler/rustc_codegen_ssa/src/back/rpath.rs
Normal file
135
compiler/rustc_codegen_ssa/src/back/rpath.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use pathdiff::diff_paths;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_middle::middle::cstore::LibSource;
|
||||
|
||||
pub struct RPathConfig<'a> {
|
||||
pub used_crates: &'a [(CrateNum, LibSource)],
|
||||
pub out_filename: PathBuf,
|
||||
pub is_like_osx: bool,
|
||||
pub has_rpath: bool,
|
||||
pub linker_is_gnu: bool,
|
||||
pub get_install_prefix_lib_path: &'a mut dyn FnMut() -> PathBuf,
|
||||
}
|
||||
|
||||
pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<String> {
|
||||
// No rpath on windows
|
||||
if !config.has_rpath {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
debug!("preparing the RPATH!");
|
||||
|
||||
let libs = config.used_crates.clone();
|
||||
let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::<Vec<_>>();
|
||||
let rpaths = get_rpaths(config, &libs);
|
||||
let mut flags = rpaths_to_flags(&rpaths);
|
||||
|
||||
// Use DT_RUNPATH instead of DT_RPATH if available
|
||||
if config.linker_is_gnu {
|
||||
flags.push("-Wl,--enable-new-dtags".to_owned());
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
|
||||
let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity
|
||||
|
||||
for rpath in rpaths {
|
||||
if rpath.contains(',') {
|
||||
ret.push("-Wl,-rpath".into());
|
||||
ret.push("-Xlinker".into());
|
||||
ret.push(rpath.clone());
|
||||
} else {
|
||||
ret.push(format!("-Wl,-rpath,{}", &(*rpath)));
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn get_rpaths(config: &mut RPathConfig<'_>, libs: &[PathBuf]) -> Vec<String> {
|
||||
debug!("output: {:?}", config.out_filename.display());
|
||||
debug!("libs:");
|
||||
for libpath in libs {
|
||||
debug!(" {:?}", libpath.display());
|
||||
}
|
||||
|
||||
// Use relative paths to the libraries. Binaries can be moved
|
||||
// as long as they maintain the relative relationship to the
|
||||
// crates they depend on.
|
||||
let rel_rpaths = get_rpaths_relative_to_output(config, libs);
|
||||
|
||||
// And a final backup rpath to the global library location.
|
||||
let fallback_rpaths = vec![get_install_prefix_rpath(config)];
|
||||
|
||||
fn log_rpaths(desc: &str, rpaths: &[String]) {
|
||||
debug!("{} rpaths:", desc);
|
||||
for rpath in rpaths {
|
||||
debug!(" {}", *rpath);
|
||||
}
|
||||
}
|
||||
|
||||
log_rpaths("relative", &rel_rpaths);
|
||||
log_rpaths("fallback", &fallback_rpaths);
|
||||
|
||||
let mut rpaths = rel_rpaths;
|
||||
rpaths.extend_from_slice(&fallback_rpaths);
|
||||
|
||||
// Remove duplicates
|
||||
minimize_rpaths(&rpaths)
|
||||
}
|
||||
|
||||
fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>, libs: &[PathBuf]) -> Vec<String> {
|
||||
libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
|
||||
}
|
||||
|
||||
fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> String {
|
||||
// Mac doesn't appear to support $ORIGIN
|
||||
let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" };
|
||||
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or_else(|_| cwd.join(lib));
|
||||
lib.pop(); // strip filename
|
||||
let mut output = cwd.join(&config.out_filename);
|
||||
output.pop(); // strip filename
|
||||
let output = fs::canonicalize(&output).unwrap_or(output);
|
||||
let relative = path_relative_from(&lib, &output)
|
||||
.unwrap_or_else(|| panic!("couldn't create relative path from {:?} to {:?}", output, lib));
|
||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||
format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path"))
|
||||
}
|
||||
|
||||
// This routine is adapted from the *old* Path's `path_relative_from`
|
||||
// function, which works differently from the new `relative_from` function.
|
||||
// In particular, this handles the case on unix where both paths are
|
||||
// absolute but with only the root as the common directory.
|
||||
fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
|
||||
diff_paths(path, base)
|
||||
}
|
||||
|
||||
fn get_install_prefix_rpath(config: &mut RPathConfig<'_>) -> String {
|
||||
let path = (config.get_install_prefix_lib_path)();
|
||||
let path = env::current_dir().unwrap().join(&path);
|
||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||
path.to_str().expect("non-utf8 component in rpath").to_owned()
|
||||
}
|
||||
|
||||
fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
|
||||
let mut set = FxHashSet::default();
|
||||
let mut minimized = Vec::new();
|
||||
for rpath in rpaths {
|
||||
if set.insert(rpath) {
|
||||
minimized.push(rpath.clone());
|
||||
}
|
||||
}
|
||||
minimized
|
||||
}
|
||||
|
||||
#[cfg(all(unix, test))]
|
||||
mod tests;
|
74
compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
Normal file
74
compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use super::RPathConfig;
|
||||
use super::{get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[test]
|
||||
fn test_rpaths_to_flags() {
|
||||
let flags = rpaths_to_flags(&["path1".to_string(), "path2".to_string()]);
|
||||
assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minimize1() {
|
||||
let res = minimize_rpaths(&["rpath1".to_string(), "rpath2".to_string(), "rpath1".to_string()]);
|
||||
assert!(res == ["rpath1", "rpath2",]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minimize2() {
|
||||
let res = minimize_rpaths(&[
|
||||
"1a".to_string(),
|
||||
"2".to_string(),
|
||||
"2".to_string(),
|
||||
"1a".to_string(),
|
||||
"4a".to_string(),
|
||||
"1a".to_string(),
|
||||
"2".to_string(),
|
||||
"3".to_string(),
|
||||
"4a".to_string(),
|
||||
"3".to_string(),
|
||||
]);
|
||||
assert!(res == ["1a", "2", "4a", "3",]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rpath_relative() {
|
||||
if cfg!(target_os = "macos") {
|
||||
let config = &mut RPathConfig {
|
||||
used_crates: &[],
|
||||
has_rpath: true,
|
||||
is_like_osx: true,
|
||||
linker_is_gnu: false,
|
||||
out_filename: PathBuf::from("bin/rustc"),
|
||||
get_install_prefix_lib_path: &mut || panic!(),
|
||||
};
|
||||
let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so"));
|
||||
assert_eq!(res, "@loader_path/../lib");
|
||||
} else {
|
||||
let config = &mut RPathConfig {
|
||||
used_crates: &[],
|
||||
out_filename: PathBuf::from("bin/rustc"),
|
||||
get_install_prefix_lib_path: &mut || panic!(),
|
||||
has_rpath: true,
|
||||
is_like_osx: false,
|
||||
linker_is_gnu: true,
|
||||
};
|
||||
let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so"));
|
||||
assert_eq!(res, "$ORIGIN/../lib");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xlinker() {
|
||||
let args = rpaths_to_flags(&["a/normal/path".to_string(), "a,comma,path".to_string()]);
|
||||
|
||||
assert_eq!(
|
||||
args,
|
||||
vec![
|
||||
"-Wl,-rpath,a/normal/path".to_string(),
|
||||
"-Wl,-rpath".to_string(),
|
||||
"-Xlinker".to_string(),
|
||||
"a,comma,path".to_string()
|
||||
]
|
||||
);
|
||||
}
|
446
compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Normal file
446
compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Normal file
|
@ -0,0 +1,446 @@
|
|||
use std::collections::hash_map::Entry::*;
|
||||
|
||||
use rustc_ast::expand::allocator::ALLOCATOR_METHODS;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::Node;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::middle::exported_symbols::{
|
||||
metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
|
||||
};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::{SymbolName, TyCtxt};
|
||||
use rustc_session::config::{CrateType, SanitizerSet};
|
||||
|
||||
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
|
||||
crates_export_threshold(&tcx.sess.crate_types())
|
||||
}
|
||||
|
||||
fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel {
|
||||
match crate_type {
|
||||
CrateType::Executable | CrateType::Staticlib | CrateType::ProcMacro | CrateType::Cdylib => {
|
||||
SymbolExportLevel::C
|
||||
}
|
||||
CrateType::Rlib | CrateType::Dylib => SymbolExportLevel::Rust,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn crates_export_threshold(crate_types: &[CrateType]) -> SymbolExportLevel {
|
||||
if crate_types
|
||||
.iter()
|
||||
.any(|&crate_type| crate_export_threshold(crate_type) == SymbolExportLevel::Rust)
|
||||
{
|
||||
SymbolExportLevel::Rust
|
||||
} else {
|
||||
SymbolExportLevel::C
|
||||
}
|
||||
}
|
||||
|
||||
fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<SymbolExportLevel> {
|
||||
assert_eq!(cnum, LOCAL_CRATE);
|
||||
|
||||
if !tcx.sess.opts.output_types.should_codegen() {
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
// Check to see if this crate is a "special runtime crate". These
|
||||
// crates, implementation details of the standard library, typically
|
||||
// have a bunch of `pub extern` and `#[no_mangle]` functions as the
|
||||
// ABI between them. We don't want their symbols to have a `C`
|
||||
// export level, however, as they're just implementation details.
|
||||
// Down below we'll hardwire all of the symbols to the `Rust` export
|
||||
// level instead.
|
||||
let special_runtime_crate =
|
||||
tcx.is_panic_runtime(LOCAL_CRATE) || tcx.is_compiler_builtins(LOCAL_CRATE);
|
||||
|
||||
let mut reachable_non_generics: DefIdMap<_> = tcx
|
||||
.reachable_set(LOCAL_CRATE)
|
||||
.iter()
|
||||
.filter_map(|&def_id| {
|
||||
// We want to ignore some FFI functions that are not exposed from
|
||||
// this crate. Reachable FFI functions can be lumped into two
|
||||
// categories:
|
||||
//
|
||||
// 1. Those that are included statically via a static library
|
||||
// 2. Those included otherwise (e.g., dynamically or via a framework)
|
||||
//
|
||||
// Although our LLVM module is not literally emitting code for the
|
||||
// statically included symbols, it's an export of our library which
|
||||
// needs to be passed on to the linker and encoded in the metadata.
|
||||
//
|
||||
// As a result, if this id is an FFI item (foreign item) then we only
|
||||
// let it through if it's included statically.
|
||||
match tcx.hir().get(tcx.hir().local_def_id_to_hir_id(def_id)) {
|
||||
Node::ForeignItem(..) => {
|
||||
tcx.is_statically_included_foreign_item(def_id).then_some(def_id)
|
||||
}
|
||||
|
||||
// Only consider nodes that actually have exported symbols.
|
||||
Node::Item(&hir::Item {
|
||||
kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn(..),
|
||||
..
|
||||
})
|
||||
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
|
||||
let generics = tcx.generics_of(def_id);
|
||||
if !generics.requires_monomorphization(tcx)
|
||||
// Functions marked with #[inline] are codegened with "internal"
|
||||
// linkage and are not exported unless marked with an extern
|
||||
// inidicator
|
||||
&& (!Instance::mono(tcx, def_id.to_def_id()).def.generates_cgu_internal_copy(tcx)
|
||||
|| tcx.codegen_fn_attrs(def_id.to_def_id()).contains_extern_indicator())
|
||||
{
|
||||
Some(def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.map(|def_id| {
|
||||
let export_level = if special_runtime_crate {
|
||||
let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name;
|
||||
// We can probably do better here by just ensuring that
|
||||
// it has hidden visibility rather than public
|
||||
// visibility, as this is primarily here to ensure it's
|
||||
// not stripped during LTO.
|
||||
//
|
||||
// In general though we won't link right if these
|
||||
// symbols are stripped, and LTO currently strips them.
|
||||
match name {
|
||||
"rust_eh_personality"
|
||||
| "rust_eh_register_frames"
|
||||
| "rust_eh_unregister_frames" =>
|
||||
SymbolExportLevel::C,
|
||||
_ => SymbolExportLevel::Rust,
|
||||
}
|
||||
} else {
|
||||
symbol_export_level(tcx, def_id.to_def_id())
|
||||
};
|
||||
debug!(
|
||||
"EXPORTED SYMBOL (local): {} ({:?})",
|
||||
tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())),
|
||||
export_level
|
||||
);
|
||||
(def_id.to_def_id(), export_level)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(id) = tcx.proc_macro_decls_static(LOCAL_CRATE) {
|
||||
reachable_non_generics.insert(id, SymbolExportLevel::C);
|
||||
}
|
||||
|
||||
if let Some(id) = tcx.plugin_registrar_fn(LOCAL_CRATE) {
|
||||
reachable_non_generics.insert(id, SymbolExportLevel::C);
|
||||
}
|
||||
|
||||
reachable_non_generics
|
||||
}
|
||||
|
||||
fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
let export_threshold = threshold(tcx);
|
||||
|
||||
if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) {
|
||||
level.is_below_threshold(export_threshold)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
tcx.reachable_non_generics(def_id.krate).contains_key(&def_id)
|
||||
}
|
||||
|
||||
fn exported_symbols_provider_local(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cnum: CrateNum,
|
||||
) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] {
|
||||
assert_eq!(cnum, LOCAL_CRATE);
|
||||
|
||||
if !tcx.sess.opts.output_types.should_codegen() {
|
||||
return &[];
|
||||
}
|
||||
|
||||
let mut symbols: Vec<_> = tcx
|
||||
.reachable_non_generics(LOCAL_CRATE)
|
||||
.iter()
|
||||
.map(|(&def_id, &level)| (ExportedSymbol::NonGeneric(def_id), level))
|
||||
.collect();
|
||||
|
||||
if tcx.entry_fn(LOCAL_CRATE).is_some() {
|
||||
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, "main"));
|
||||
|
||||
symbols.push((exported_symbol, SymbolExportLevel::C));
|
||||
}
|
||||
|
||||
if tcx.allocator_kind().is_some() {
|
||||
for method in ALLOCATOR_METHODS {
|
||||
let symbol_name = format!("__rust_{}", method.name);
|
||||
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
|
||||
|
||||
symbols.push((exported_symbol, SymbolExportLevel::Rust));
|
||||
}
|
||||
}
|
||||
|
||||
if tcx.sess.opts.debugging_opts.instrument_coverage
|
||||
|| tcx.sess.opts.cg.profile_generate.enabled()
|
||||
{
|
||||
// These are weak symbols that point to the profile version and the
|
||||
// profile name, which need to be treated as exported so LTO doesn't nix
|
||||
// them.
|
||||
const PROFILER_WEAK_SYMBOLS: [&str; 2] =
|
||||
["__llvm_profile_raw_version", "__llvm_profile_filename"];
|
||||
|
||||
symbols.extend(PROFILER_WEAK_SYMBOLS.iter().map(|sym| {
|
||||
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym));
|
||||
(exported_symbol, SymbolExportLevel::C)
|
||||
}));
|
||||
}
|
||||
|
||||
if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
// Similar to profiling, preserve weak msan symbol during LTO.
|
||||
const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"];
|
||||
|
||||
symbols.extend(MSAN_WEAK_SYMBOLS.iter().map(|sym| {
|
||||
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym));
|
||||
(exported_symbol, SymbolExportLevel::C)
|
||||
}));
|
||||
}
|
||||
|
||||
if tcx.sess.crate_types().contains(&CrateType::Dylib) {
|
||||
let symbol_name = metadata_symbol_name(tcx);
|
||||
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
|
||||
|
||||
symbols.push((exported_symbol, SymbolExportLevel::Rust));
|
||||
}
|
||||
|
||||
if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() {
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
|
||||
use rustc_middle::ty::InstanceDef;
|
||||
|
||||
// Normally, we require that shared monomorphizations are not hidden,
|
||||
// because if we want to re-use a monomorphization from a Rust dylib, it
|
||||
// needs to be exported.
|
||||
// However, on platforms that don't allow for Rust dylibs, having
|
||||
// external linkage is enough for monomorphization to be linked to.
|
||||
let need_visibility = tcx.sess.target.target.options.dynamic_linking
|
||||
&& !tcx.sess.target.target.options.only_cdylib;
|
||||
|
||||
let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
|
||||
|
||||
for (mono_item, &(linkage, visibility)) in cgus.iter().flat_map(|cgu| cgu.items().iter()) {
|
||||
if linkage != Linkage::External {
|
||||
// We can only re-use things with external linkage, otherwise
|
||||
// we'll get a linker error
|
||||
continue;
|
||||
}
|
||||
|
||||
if need_visibility && visibility == Visibility::Hidden {
|
||||
// If we potentially share things from Rust dylibs, they must
|
||||
// not be hidden
|
||||
continue;
|
||||
}
|
||||
|
||||
match *mono_item {
|
||||
MonoItem::Fn(Instance { def: InstanceDef::Item(def), substs }) => {
|
||||
if substs.non_erasable_generics().next().is_some() {
|
||||
let symbol = ExportedSymbol::Generic(def.did, substs);
|
||||
symbols.push((symbol, SymbolExportLevel::Rust));
|
||||
}
|
||||
}
|
||||
MonoItem::Fn(Instance { def: InstanceDef::DropGlue(_, Some(ty)), substs }) => {
|
||||
// A little sanity-check
|
||||
debug_assert_eq!(
|
||||
substs.non_erasable_generics().next(),
|
||||
Some(GenericArgKind::Type(ty))
|
||||
);
|
||||
symbols.push((ExportedSymbol::DropGlue(ty), SymbolExportLevel::Rust));
|
||||
}
|
||||
_ => {
|
||||
// Any other symbols don't qualify for sharing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort so we get a stable incr. comp. hash.
|
||||
symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx));
|
||||
|
||||
tcx.arena.alloc_from_iter(symbols)
|
||||
}
|
||||
|
||||
fn upstream_monomorphizations_provider(
|
||||
tcx: TyCtxt<'_>,
|
||||
cnum: CrateNum,
|
||||
) -> DefIdMap<FxHashMap<SubstsRef<'_>, CrateNum>> {
|
||||
debug_assert!(cnum == LOCAL_CRATE);
|
||||
|
||||
let cnums = tcx.all_crate_nums(LOCAL_CRATE);
|
||||
|
||||
let mut instances: DefIdMap<FxHashMap<_, _>> = Default::default();
|
||||
|
||||
let cnum_stable_ids: IndexVec<CrateNum, Fingerprint> = {
|
||||
let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO, cnums.len() + 1);
|
||||
|
||||
for &cnum in cnums.iter() {
|
||||
cnum_stable_ids[cnum] =
|
||||
tcx.def_path_hash(DefId { krate: cnum, index: CRATE_DEF_INDEX }).0;
|
||||
}
|
||||
|
||||
cnum_stable_ids
|
||||
};
|
||||
|
||||
let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
|
||||
|
||||
for &cnum in cnums.iter() {
|
||||
for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
|
||||
let (def_id, substs) = match *exported_symbol {
|
||||
ExportedSymbol::Generic(def_id, substs) => (def_id, substs),
|
||||
ExportedSymbol::DropGlue(ty) => {
|
||||
if let Some(drop_in_place_fn_def_id) = drop_in_place_fn_def_id {
|
||||
(drop_in_place_fn_def_id, tcx.intern_substs(&[ty.into()]))
|
||||
} else {
|
||||
// `drop_in_place` in place does not exist, don't try
|
||||
// to use it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ExportedSymbol::NonGeneric(..) | ExportedSymbol::NoDefId(..) => {
|
||||
// These are no monomorphizations
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let substs_map = instances.entry(def_id).or_default();
|
||||
|
||||
match substs_map.entry(substs) {
|
||||
Occupied(mut e) => {
|
||||
// If there are multiple monomorphizations available,
|
||||
// we select one deterministically.
|
||||
let other_cnum = *e.get();
|
||||
if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] {
|
||||
e.insert(cnum);
|
||||
}
|
||||
}
|
||||
Vacant(e) => {
|
||||
e.insert(cnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instances
|
||||
}
|
||||
|
||||
fn upstream_monomorphizations_for_provider(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: DefId,
|
||||
) -> Option<&FxHashMap<SubstsRef<'_>, CrateNum>> {
|
||||
debug_assert!(!def_id.is_local());
|
||||
tcx.upstream_monomorphizations(LOCAL_CRATE).get(&def_id)
|
||||
}
|
||||
|
||||
fn upstream_drop_glue_for_provider<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
substs: SubstsRef<'tcx>,
|
||||
) -> Option<CrateNum> {
|
||||
if let Some(def_id) = tcx.lang_items().drop_in_place_fn() {
|
||||
tcx.upstream_monomorphizations_for(def_id).and_then(|monos| monos.get(&substs).cloned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
if let Some(def_id) = def_id.as_local() {
|
||||
!tcx.reachable_set(LOCAL_CRATE).contains(&def_id)
|
||||
} else {
|
||||
bug!("is_unreachable_local_definition called with non-local DefId: {:?}", def_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
providers.reachable_non_generics = reachable_non_generics_provider;
|
||||
providers.is_reachable_non_generic = is_reachable_non_generic_provider_local;
|
||||
providers.exported_symbols = exported_symbols_provider_local;
|
||||
providers.upstream_monomorphizations = upstream_monomorphizations_provider;
|
||||
providers.is_unreachable_local_definition = is_unreachable_local_definition_provider;
|
||||
providers.upstream_drop_glue_for = upstream_drop_glue_for_provider;
|
||||
}
|
||||
|
||||
pub fn provide_extern(providers: &mut Providers) {
|
||||
providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
|
||||
providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider;
|
||||
}
|
||||
|
||||
fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel {
|
||||
// We export anything that's not mangled at the "C" layer as it probably has
|
||||
// to do with ABI concerns. We do not, however, apply such treatment to
|
||||
// special symbols in the standard library for various plumbing between
|
||||
// core/std/allocators/etc. For example symbols used to hook up allocation
|
||||
// are not considered for export
|
||||
let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id);
|
||||
let is_extern = codegen_fn_attrs.contains_extern_indicator();
|
||||
let std_internal =
|
||||
codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
|
||||
|
||||
if is_extern && !std_internal {
|
||||
let target = &tcx.sess.target.target.llvm_target;
|
||||
// WebAssembly cannot export data symbols, so reduce their export level
|
||||
if target.contains("emscripten") {
|
||||
if let Some(Node::Item(&hir::Item { kind: hir::ItemKind::Static(..), .. })) =
|
||||
tcx.hir().get_if_local(sym_def_id)
|
||||
{
|
||||
return SymbolExportLevel::Rust;
|
||||
}
|
||||
}
|
||||
|
||||
SymbolExportLevel::C
|
||||
} else {
|
||||
SymbolExportLevel::Rust
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the symbol name of the given instance instantiated in a specific crate.
|
||||
pub fn symbol_name_for_instance_in_crate<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
symbol: ExportedSymbol<'tcx>,
|
||||
instantiating_crate: CrateNum,
|
||||
) -> String {
|
||||
// If this is something instantiated in the local crate then we might
|
||||
// already have cached the name as a query result.
|
||||
if instantiating_crate == LOCAL_CRATE {
|
||||
return symbol.symbol_name_for_local_instance(tcx).to_string();
|
||||
}
|
||||
|
||||
// This is something instantiated in an upstream crate, so we have to use
|
||||
// the slower (because uncached) version of computing the symbol name.
|
||||
match symbol {
|
||||
ExportedSymbol::NonGeneric(def_id) => {
|
||||
rustc_symbol_mangling::symbol_name_for_instance_in_crate(
|
||||
tcx,
|
||||
Instance::mono(tcx, def_id),
|
||||
instantiating_crate,
|
||||
)
|
||||
}
|
||||
ExportedSymbol::Generic(def_id, substs) => {
|
||||
rustc_symbol_mangling::symbol_name_for_instance_in_crate(
|
||||
tcx,
|
||||
Instance::new(def_id, substs),
|
||||
instantiating_crate,
|
||||
)
|
||||
}
|
||||
ExportedSymbol::DropGlue(ty) => rustc_symbol_mangling::symbol_name_for_instance_in_crate(
|
||||
tcx,
|
||||
Instance::resolve_drop_in_place(tcx, ty),
|
||||
instantiating_crate,
|
||||
),
|
||||
ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(),
|
||||
}
|
||||
}
|
1859
compiler/rustc_codegen_ssa/src/back/write.rs
Normal file
1859
compiler/rustc_codegen_ssa/src/back/write.rs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue