summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_ssa/src/back
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_ssa/src/back')
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs289
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs173
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs386
4 files changed, 571 insertions, 302 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 8a00c42a0..b603a8787 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -5,14 +5,14 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{ErrorGuaranteed, Handler};
-use rustc_fs_util::fix_windows_verbatim_for_gcc;
+use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library;
-use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME};
+use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME};
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
-use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
+use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip};
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
use rustc_session::cstore::DllImport;
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
@@ -23,7 +23,7 @@ use rustc_session::utils::NativeLibKind;
use rustc_session::{filesearch, Session};
use rustc_span::symbol::Symbol;
use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
-use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy};
+use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy};
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
@@ -68,6 +68,7 @@ pub fn link_binary<'a>(
) -> Result<(), ErrorGuaranteed> {
let _timer = sess.timer("link_binary");
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
+ let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
for &crate_type in sess.crate_types().iter() {
// Ignore executable crates if we have -Z no-codegen, as they will error.
if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())
@@ -97,12 +98,15 @@ pub fn link_binary<'a>(
.tempdir()
.unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error }));
let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
- let out_filename = out_filename(
+ let output = out_filename(
sess,
crate_type,
outputs,
codegen_results.crate_info.local_crate_name,
);
+ let crate_name = format!("{}", codegen_results.crate_info.local_crate_name);
+ let out_filename =
+ output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str()));
match crate_type {
CrateType::Rlib => {
let _timer = sess.timer("link_rlib");
@@ -152,6 +156,17 @@ pub fn link_binary<'a>(
);
}
}
+
+ if output.is_stdout() {
+ if output.is_tty() {
+ sess.emit_err(errors::BinaryOutputToTty {
+ shorthand: OutputType::Exe.shorthand(),
+ });
+ } else if let Err(e) = copy_to_stdout(&out_filename) {
+ sess.emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e));
+ }
+ tempfiles_for_stdout_output.push(out_filename);
+ }
}
}
@@ -189,6 +204,11 @@ pub fn link_binary<'a>(
remove_temps_from_module(allocator_module);
}
+ // Remove the temporary files if output goes to stdout
+ for temp in tempfiles_for_stdout_output {
+ ensure_removed(sess.diagnostic(), &temp);
+ }
+
// If no requested outputs require linking, then the object temporaries should
// be kept.
if !sess.opts.output_types.should_link() {
@@ -893,7 +913,7 @@ fn link_natively<'a>(
linker_path: &linker_path,
exit_status: prog.status,
command: &cmd,
- escaped_output: &escaped_output,
+ escaped_output,
};
sess.diagnostic().emit_err(err);
// If MSVC's `link.exe` was expected but the return code
@@ -1188,6 +1208,9 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d
if sanitizer.contains(SanitizerSet::HWADDRESS) {
link_sanitizer_runtime(sess, linker, "hwasan");
}
+ if sanitizer.contains(SanitizerSet::SAFESTACK) {
+ link_sanitizer_runtime(sess, linker, "safestack");
+ }
}
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
@@ -1299,44 +1322,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
sess.emit_fatal(errors::LinkerFileStem);
});
-
- // Remove any version postfix.
- let stem = stem
- .rsplit_once('-')
- .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs))
- .unwrap_or(stem);
-
- // GCC/Clang can have an optional target prefix.
- let flavor = if stem == "emcc" {
- LinkerFlavor::EmCc
- } else if stem == "gcc"
- || stem.ends_with("-gcc")
- || stem == "g++"
- || stem.ends_with("-g++")
- || stem == "clang"
- || stem.ends_with("-clang")
- || stem == "clang++"
- || stem.ends_with("-clang++")
- {
- LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target)
- } else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") {
- LinkerFlavor::WasmLld(Cc::No)
- } else if stem == "ld" || stem.ends_with("-ld") {
- LinkerFlavor::from_cli(LinkerFlavorCli::Ld, &sess.target)
- } else if stem == "ld.lld" {
- LinkerFlavor::Gnu(Cc::No, Lld::Yes)
- } else if stem == "link" {
- LinkerFlavor::Msvc(Lld::No)
- } else if stem == "lld-link" {
- LinkerFlavor::Msvc(Lld::Yes)
- } else if stem == "lld" || stem == "rust-lld" {
- let lld_flavor = sess.target.linker_flavor.lld_flavor();
- LinkerFlavor::from_cli(LinkerFlavorCli::Lld(lld_flavor), &sess.target)
- } else {
- // fall back to the value in the target spec
- sess.target.linker_flavor
- };
-
+ let flavor = sess.target.linker_flavor.with_linker_hints(stem);
Some((linker, flavor))
}
(None, None) => None,
@@ -1346,7 +1332,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
// linker and linker flavor specified via command line have precedence over what the target
// specification specifies
let linker_flavor =
- sess.opts.cg.linker_flavor.map(|flavor| LinkerFlavor::from_cli(flavor, &sess.target));
+ sess.opts.cg.linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor));
if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) {
return ret;
}
@@ -1702,7 +1688,7 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
/// instead of being found somewhere on the host system.
/// We only provide such support for a very limited number of targets.
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
- if let Some(self_contained) = sess.opts.cg.link_self_contained {
+ if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
sess.emit_err(errors::UnsupportedLinkSelfContained);
}
@@ -2131,7 +2117,14 @@ fn linker_with_args<'a>(
cmd.add_as_needed();
// Local native libraries of all kinds.
- add_local_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir);
+ add_local_native_libraries(
+ cmd,
+ sess,
+ archive_builder_builder,
+ codegen_results,
+ tmpdir,
+ link_output_kind,
+ );
// Upstream rust crates and their non-dynamic native libraries.
add_upstream_rust_crates(
@@ -2141,10 +2134,18 @@ fn linker_with_args<'a>(
codegen_results,
crate_type,
tmpdir,
+ link_output_kind,
);
// Dynamic native libraries from upstream crates.
- add_upstream_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir);
+ add_upstream_native_libraries(
+ cmd,
+ sess,
+ archive_builder_builder,
+ codegen_results,
+ tmpdir,
+ link_output_kind,
+ );
// Link with the import library generated for any raw-dylib functions.
for (raw_dylib_name, raw_dylib_imports) in
@@ -2245,7 +2246,8 @@ fn add_order_independent_options(
out_filename: &Path,
tmpdir: &Path,
) {
- add_gcc_ld_path(cmd, sess, flavor);
+ // Take care of the flavors and CLI options requesting the `lld` linker.
+ add_lld_args(cmd, sess, flavor);
add_apple_sdk(cmd, sess, flavor);
@@ -2290,11 +2292,13 @@ fn add_order_independent_options(
} else if flavor == LinkerFlavor::Bpf {
cmd.arg("--cpu");
cmd.arg(&codegen_results.crate_info.target_cpu);
- cmd.arg("--cpu-features");
- cmd.arg(match &sess.opts.cg.target_feature {
- feat if !feat.is_empty() => feat.as_ref(),
- _ => sess.target.options.features.as_ref(),
- });
+ if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features]
+ .into_iter()
+ .find(|feat| !feat.is_empty())
+ {
+ cmd.arg("--cpu-features");
+ cmd.arg(feat);
+ }
}
cmd.linker_plugin_lto();
@@ -2399,6 +2403,7 @@ fn add_native_libs_from_crate(
cnum: CrateNum,
link_static: bool,
link_dynamic: bool,
+ link_output_kind: LinkOutputKind,
) {
if !sess.opts.unstable_opts.link_native_libraries {
// If `-Zlink-native-libraries=false` is set, then the assumption is that an
@@ -2478,8 +2483,16 @@ fn add_native_libs_from_crate(
}
}
NativeLibKind::Unspecified => {
- if link_dynamic {
- cmd.link_dylib(name, verbatim, true);
+ // If we are generating a static binary, prefer static library when the
+ // link kind is unspecified.
+ if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs {
+ if link_static {
+ cmd.link_staticlib(name, verbatim)
+ }
+ } else {
+ if link_dynamic {
+ cmd.link_dylib(name, verbatim, true);
+ }
}
}
NativeLibKind::Framework { as_needed } => {
@@ -2506,6 +2519,7 @@ fn add_local_native_libraries(
archive_builder_builder: &dyn ArchiveBuilderBuilder,
codegen_results: &CodegenResults,
tmpdir: &Path,
+ link_output_kind: LinkOutputKind,
) {
if sess.opts.unstable_opts.link_native_libraries {
// User-supplied library search paths (-L on the command line). These are the same paths
@@ -2535,6 +2549,7 @@ fn add_local_native_libraries(
LOCAL_CRATE,
link_static,
link_dynamic,
+ link_output_kind,
);
}
@@ -2545,6 +2560,7 @@ fn add_upstream_rust_crates<'a>(
codegen_results: &CodegenResults,
crate_type: CrateType,
tmpdir: &Path,
+ link_output_kind: LinkOutputKind,
) {
// All of the heavy lifting has previously been accomplished by the
// dependency_format module of the compiler. This is just crawling the
@@ -2622,6 +2638,7 @@ fn add_upstream_rust_crates<'a>(
cnum,
link_static,
link_dynamic,
+ link_output_kind,
);
}
}
@@ -2632,6 +2649,7 @@ fn add_upstream_native_libraries(
archive_builder_builder: &dyn ArchiveBuilderBuilder,
codegen_results: &CodegenResults,
tmpdir: &Path,
+ link_output_kind: LinkOutputKind,
) {
let search_path = OnceCell::new();
for &cnum in &codegen_results.crate_info.used_crates {
@@ -2660,10 +2678,35 @@ fn add_upstream_native_libraries(
cnum,
link_static,
link_dynamic,
+ link_output_kind,
);
}
}
+// Rehome lib paths (which exclude the library file name) that point into the sysroot lib directory
+// to be relative to the sysroot directory, which may be a relative path specified by the user.
+//
+// If the sysroot is a relative path, and the sysroot libs are specified as an absolute path, the
+// linker command line can be non-deterministic due to the paths including the current working
+// directory. The linker command line needs to be deterministic since it appears inside the PDB
+// file generated by the MSVC linker. See https://github.com/rust-lang/rust/issues/112586.
+//
+// The returned path will always have `fix_windows_verbatim_for_gcc()` applied to it.
+fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf {
+ let sysroot_lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
+ let canonical_sysroot_lib_path =
+ { try_canonicalize(&sysroot_lib_path).unwrap_or_else(|_| sysroot_lib_path.clone()) };
+
+ let canonical_lib_dir = try_canonicalize(lib_dir).unwrap_or_else(|_| lib_dir.to_path_buf());
+ if canonical_lib_dir == canonical_sysroot_lib_path {
+ // This path, returned by `target_filesearch().get_lib_path()`, has
+ // already had `fix_windows_verbatim_for_gcc()` applied if needed.
+ sysroot_lib_path
+ } else {
+ fix_windows_verbatim_for_gcc(&lib_dir)
+ }
+}
+
// Adds the static "rlib" versions of all crates to the command line.
// There's a bit of magic which happens here specifically related to LTO,
// namely that we remove upstream object files.
@@ -2695,7 +2738,13 @@ fn add_static_crate<'a>(
let cratepath = &src.rlib.as_ref().unwrap().0;
let mut link_upstream = |path: &Path| {
- cmd.link_rlib(&fix_windows_verbatim_for_gcc(path));
+ let rlib_path = if let Some(dir) = path.parent() {
+ let file_name = path.file_name().expect("rlib path has no file name path component");
+ rehome_sysroot_lib_dir(sess, &dir).join(file_name)
+ } else {
+ fix_windows_verbatim_for_gcc(path)
+ };
+ cmd.link_rlib(&rlib_path);
};
if !are_upstream_rust_objects_already_included(sess)
@@ -2764,7 +2813,7 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
// what its name is
let parent = cratepath.parent();
if let Some(dir) = parent {
- cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
+ cmd.include_path(&rehome_sysroot_lib_dir(sess, dir));
}
let stem = cratepath.file_stem().unwrap().to_str().unwrap();
// Convert library file-stem into a cc -l argument.
@@ -2900,55 +2949,81 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro
}
}
-fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
- if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld {
- if let LinkerFlavor::Gnu(Cc::Yes, _)
- | LinkerFlavor::Darwin(Cc::Yes, _)
- | LinkerFlavor::WasmLld(Cc::Yes) = flavor
- {
- match ld_impl {
- LdImpl::Lld => {
- // Implement the "self-contained" part of -Zgcc-ld
- // by adding rustc distribution directories to the tool search path.
- for path in sess.get_tools_search_paths(false) {
- cmd.arg({
- let mut arg = OsString::from("-B");
- arg.push(path.join("gcc-ld"));
- arg
- });
- }
- // Implement the "linker flavor" part of -Zgcc-ld
- // by asking cc to use some kind of lld.
- cmd.arg("-fuse-ld=lld");
-
- if !flavor.is_gnu() {
- // Tell clang to use a non-default LLD flavor.
- // Gcc doesn't understand the target option, but we currently assume
- // that gcc is not used for Apple and Wasm targets (#97402).
- //
- // Note that we don't want to do that by default on macOS: e.g. passing a
- // 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
- // shown in issue #101653 and the discussion in PR #101792.
- //
- // It could be required in some cases of cross-compiling with
- // `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
- // which specific versions of clang, macOS SDK, host and target OS
- // combinations impact us here.
- //
- // So we do a simple first-approximation until we know more of what the
- // Apple targets require (and which would be handled prior to hitting this
- // `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
- // this should be manually passed if needed. We specify the target when
- // targeting a different linker flavor on macOS, and that's also always
- // the case when targeting WASM.
- if sess.target.linker_flavor != sess.host.linker_flavor {
- cmd.arg(format!("--target={}", sess.target.llvm_target));
- }
- }
- }
- }
- } else {
- sess.emit_fatal(errors::OptionGccOnly);
+/// When using the linker flavors opting in to `lld`, or the unstable `-Zgcc-ld=lld` flag, add the
+/// necessary paths and arguments to invoke it:
+/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc,
+/// - or any `lld` available to `cc`.
+fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
+ let unstable_use_lld = sess.opts.unstable_opts.gcc_ld.is_some();
+ debug!("add_lld_args requested, flavor: '{flavor:?}', `-Zgcc-ld=lld`: {unstable_use_lld}");
+
+ // Sanity check: using the old unstable `-Zgcc-ld=lld` option requires a `cc`-using flavor.
+ let flavor_uses_cc = flavor.uses_cc();
+ if unstable_use_lld && !flavor_uses_cc {
+ sess.emit_fatal(errors::OptionGccOnly);
+ }
+
+ // If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`,
+ // we don't need to do anything.
+ let use_lld = flavor.uses_lld() || unstable_use_lld;
+ if !flavor_uses_cc || !use_lld {
+ return;
+ }
+
+ let self_contained_linker = sess.opts.cg.link_self_contained.linker();
+
+ // FIXME: some targets default to using `lld`, but users can only override the linker on the CLI
+ // and cannot yet select the precise linker flavor to opt out of that. See for example issue
+ // #113597 for the `thumbv6m-none-eabi` target: a driver is used, and its default linker
+ // conflicts with the target's flavor, causing unexpected arguments being passed.
+ //
+ // Until the new `LinkerFlavor`-like CLI options are stabilized, we only adopt MCP510's behavior
+ // if its dedicated unstable CLI flags are used, to keep the current sub-optimal stable
+ // behavior.
+ let using_mcp510 =
+ self_contained_linker || sess.opts.cg.linker_flavor.is_some_and(|f| f.is_unstable());
+ if !using_mcp510 && !unstable_use_lld {
+ return;
+ }
+
+ // 1. Implement the "self-contained" part of this feature by adding rustc distribution
+ // directories to the tool's search path.
+ if self_contained_linker || unstable_use_lld {
+ for path in sess.get_tools_search_paths(false) {
+ cmd.arg({
+ let mut arg = OsString::from("-B");
+ arg.push(path.join("gcc-ld"));
+ arg
+ });
+ }
+ }
+
+ // 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of
+ // `lld` as the linker.
+ cmd.arg("-fuse-ld=lld");
+
+ if !flavor.is_gnu() {
+ // Tell clang to use a non-default LLD flavor.
+ // Gcc doesn't understand the target option, but we currently assume
+ // that gcc is not used for Apple and Wasm targets (#97402).
+ //
+ // Note that we don't want to do that by default on macOS: e.g. passing a
+ // 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
+ // shown in issue #101653 and the discussion in PR #101792.
+ //
+ // It could be required in some cases of cross-compiling with
+ // `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
+ // which specific versions of clang, macOS SDK, host and target OS
+ // combinations impact us here.
+ //
+ // So we do a simple first-approximation until we know more of what the
+ // Apple targets require (and which would be handled prior to hitting this
+ // `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
+ // this should be manually passed if needed. We specify the target when
+ // targeting a different linker flavor on macOS, and that's also always
+ // the case when targeting WASM.
+ if sess.target.linker_flavor != sess.host.linker_flavor {
+ cmd.arg(format!("--target={}", sess.target.llvm_target));
}
}
}
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index cd56f85cc..8ac86fa4b 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -13,6 +13,7 @@ use std::{env, mem, str};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library;
use rustc_middle::middle::dependency_format::Linkage;
+use rustc_middle::middle::exported_symbols;
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
@@ -659,8 +660,6 @@ impl<'a> Linker for GccLinker<'a> {
return;
}
- // FIXME(#99978) hide #[no_mangle] symbols for proc-macros
-
let is_windows = self.sess.target.is_like_windows;
let path = tmpdir.join(if is_windows { "list.def" } else { "list" });
@@ -1679,8 +1678,15 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
return exports.iter().map(ToString::to_string).collect();
}
- let mut symbols = Vec::new();
+ if let CrateType::ProcMacro = crate_type {
+ exported_symbols_for_proc_macro_crate(tcx)
+ } else {
+ exported_symbols_for_non_proc_macro(tcx, crate_type)
+ }
+}
+fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
+ let mut symbols = Vec::new();
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) {
@@ -1691,6 +1697,19 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
symbols
}
+fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
+ // `exported_symbols` will be empty when !should_codegen.
+ if !tcx.sess.opts.output_types.should_codegen() {
+ return Vec::new();
+ }
+
+ let stable_crate_id = tcx.sess.local_stable_crate_id();
+ let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
+ let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);
+
+ vec![proc_macro_decls_name, metadata_symbol_name]
+}
+
pub(crate) fn linked_symbols(
tcx: TyCtxt<'_>,
crate_type: CrateType,
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index ad27b854d..00e6acb5c 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -6,8 +6,8 @@ use std::path::Path;
use object::write::{self, StandardSegment, Symbol, SymbolSection};
use object::{
- elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
- SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
+ elf, pe, xcoff, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
+ ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
};
use snap::write::FrameEncoder;
@@ -20,7 +20,7 @@ use rustc_metadata::EncodedMetadata;
use rustc_session::cstore::MetadataLoader;
use rustc_session::Session;
use rustc_target::abi::Endian;
-use rustc_target::spec::{RelocModel, Target};
+use rustc_target::spec::{ef_avr_arch, RelocModel, Target};
/// The default metadata loader. This is used by cg_llvm and cg_clif.
///
@@ -35,6 +35,8 @@ use rustc_target::spec::{RelocModel, Target};
#[derive(Debug)]
pub struct DefaultMetadataLoader;
+static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata";
+
fn load_metadata_with(
path: &Path,
f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
@@ -48,7 +50,7 @@ fn load_metadata_with(
}
impl MetadataLoader for DefaultMetadataLoader {
- fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
+ fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
load_metadata_with(path, |data| {
let archive = object::read::archive::ArchiveFile::parse(&*data)
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
@@ -60,7 +62,11 @@ impl MetadataLoader for DefaultMetadataLoader {
let data = entry
.data(data)
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
- return search_for_section(path, data, ".rmeta");
+ if target.is_like_aix {
+ return get_metadata_xcoff(path, data);
+ } else {
+ return search_for_section(path, data, ".rmeta");
+ }
}
}
@@ -68,8 +74,12 @@ impl MetadataLoader for DefaultMetadataLoader {
})
}
- fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
- load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
+ fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
+ if target.is_like_aix {
+ load_metadata_with(path, |data| get_metadata_xcoff(path, data))
+ } else {
+ load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
+ }
}
}
@@ -141,6 +151,33 @@ fn add_gnu_property_note(
file.append_section_data(section, &data, 8);
}
+pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
+ let Ok(file) = object::File::parse(data) else {
+ return Ok(data);
+ };
+ let info_data = search_for_section(path, data, ".info")?;
+ if let Some(metadata_symbol) =
+ file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
+ {
+ let offset = metadata_symbol.address() as usize;
+ if offset < 4 {
+ return Err(format!("Invalid metadata symbol offset: {}", offset));
+ }
+ // The offset specifies the location of rustc metadata in the comment section.
+ // The metadata is preceded by a 4-byte length field.
+ let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
+ if offset + len > (info_data.len() as usize) {
+ return Err(format!(
+ "Metadata at offset {} with size {} is beyond .info section",
+ offset, len
+ ));
+ }
+ return Ok(&info_data[offset..(offset + len)]);
+ } else {
+ return Err(format!("Unable to find symbol {}", AIX_METADATA_SYMBOL_NAME));
+ };
+}
+
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
let endianness = match sess.target.options.endian {
Endian::Little => Endianness::Little,
@@ -183,6 +220,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
BinaryFormat::MachO
} else if sess.target.is_like_windows {
BinaryFormat::Coff
+ } else if sess.target.is_like_aix {
+ BinaryFormat::Xcoff
} else {
BinaryFormat::Elf
};
@@ -245,8 +284,24 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
e_flags
}
Architecture::LoongArch64 => {
- // Source: https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_e_flags_identifies_abi_type_and_version
- elf::EF_LARCH_OBJABI_V1 | elf::EF_LARCH_ABI_DOUBLE_FLOAT
+ // Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
+ let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
+ let features = &sess.target.options.features;
+
+ // Select the appropriate floating-point ABI
+ if features.contains("+d") {
+ e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT;
+ } else if features.contains("+f") {
+ e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT;
+ } else {
+ e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT;
+ }
+ e_flags
+ }
+ Architecture::Avr => {
+ // Resolve the ISA revision and set
+ // the appropriate EF_AVR_ARCH flag.
+ ef_avr_arch(&sess.target.options.cpu)
}
_ => 0,
};
@@ -351,11 +406,15 @@ pub fn create_wrapper_file(
// to add a case above.
return (data.to_vec(), MetadataPosition::Last);
};
- let section = file.add_section(
- file.segment_name(StandardSegment::Debug).to_vec(),
- section_name,
- SectionKind::Debug,
- );
+ let section = if file.format() == BinaryFormat::Xcoff {
+ file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
+ } else {
+ file.add_section(
+ file.segment_name(StandardSegment::Debug).to_vec(),
+ section_name,
+ SectionKind::Debug,
+ )
+ };
match file.format() {
BinaryFormat::Coff => {
file.section_mut(section).flags =
@@ -365,6 +424,31 @@ pub fn create_wrapper_file(
file.section_mut(section).flags =
SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
}
+ BinaryFormat::Xcoff => {
+ // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
+ file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
+ file.section_mut(section).flags =
+ SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
+
+ let len = data.len() as u32;
+ let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
+ // Add a symbol referring to the data in .info section.
+ file.add_symbol(Symbol {
+ name: AIX_METADATA_SYMBOL_NAME.into(),
+ value: offset + 4,
+ size: 0,
+ kind: SymbolKind::Unknown,
+ scope: SymbolScope::Compilation,
+ weak: false,
+ section: SymbolSection::Section(section),
+ flags: SymbolFlags::Xcoff {
+ n_sclass: xcoff::C_INFO,
+ x_smtyp: xcoff::C_HIDEXT,
+ x_smclas: xcoff::C_HIDEXT,
+ containing_csect: None,
+ },
+ });
+ }
_ => {}
};
file.append_section_data(section, data, 1);
@@ -401,6 +485,9 @@ pub fn create_compressed_metadata_file(
let Some(mut file) = create_object_file(sess) else {
return compressed.to_vec();
};
+ if file.format() == BinaryFormat::Xcoff {
+ return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name);
+ }
let section = file.add_section(
file.segment_name(StandardSegment::Data).to_vec(),
b".rustc".to_vec(),
@@ -430,3 +517,61 @@ pub fn create_compressed_metadata_file(
file.write().unwrap()
}
+
+/// * Xcoff - On AIX, custom sections are merged into predefined sections,
+/// so custom .rustc section is not preserved during linking.
+/// For this reason, we store metadata in predefined .info section, and
+/// define a symbol to reference the metadata. To preserve metadata during
+/// linking on AIX, we have to
+/// 1. Create an empty .text section, a empty .data section.
+/// 2. Define an empty symbol named `symbol_name` inside .data section.
+/// 3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
+/// data inside .info section.
+/// From XCOFF's view, (2) creates a csect entry in the symbol table, the
+/// symbol created by (3) is a info symbol for the preceding csect. Thus
+/// two symbols are preserved during linking and we can use the second symbol
+/// to reference the metadata.
+pub fn create_compressed_metadata_file_for_xcoff(
+ mut file: write::Object<'_>,
+ data: &[u8],
+ symbol_name: &str,
+) -> Vec<u8> {
+ assert!(file.format() == BinaryFormat::Xcoff);
+ // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
+ file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
+ let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
+ let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
+ file.add_file_symbol("lib.rmeta".into());
+ file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
+ // Add a global symbol to data_section.
+ file.add_symbol(Symbol {
+ name: symbol_name.as_bytes().into(),
+ value: 0,
+ size: 0,
+ kind: SymbolKind::Data,
+ scope: SymbolScope::Dynamic,
+ weak: true,
+ section: SymbolSection::Section(data_section),
+ flags: SymbolFlags::None,
+ });
+ let len = data.len() as u32;
+ let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
+ // Add a symbol referring to the rustc metadata.
+ file.add_symbol(Symbol {
+ name: AIX_METADATA_SYMBOL_NAME.into(),
+ value: offset + 4, // The metadata is preceded by a 4-byte length field.
+ size: 0,
+ kind: SymbolKind::Unknown,
+ scope: SymbolScope::Dynamic,
+ weak: false,
+ section: SymbolSection::Section(section),
+ flags: SymbolFlags::Xcoff {
+ n_sclass: xcoff::C_INFO,
+ x_smtyp: xcoff::C_HIDEXT,
+ x_smclas: xcoff::C_HIDEXT,
+ containing_csect: None,
+ },
+ });
+ file.append_section_data(section, data, 1);
+ file.write().unwrap()
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index c323372bd..ececa29b2 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -9,11 +9,9 @@ use crate::{
};
use jobserver::{Acquired, Client};
use rustc_ast::attr;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::memmap::Mmap;
-use rustc_data_structures::profiling::SelfProfilerRef;
-use rustc_data_structures::profiling::TimingGuard;
-use rustc_data_structures::profiling::VerboseTimingGuard;
+use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::Emitter;
use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
@@ -23,12 +21,13 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_incremental::{
copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
};
+use rustc_metadata::fs::copy_to_stdout;
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::middle::exported_symbols::SymbolExportInfo;
use rustc_middle::ty::TyCtxt;
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
-use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType};
+use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType};
use rustc_session::config::{Passes, SwitchWithOptPath};
use rustc_session::Session;
use rustc_span::source_map::SourceMap;
@@ -36,6 +35,7 @@ use rustc_span::symbol::sym;
use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span};
use rustc_target::spec::{MergeFunctions, SanitizerSet};
+use crate::errors::ErrorCreatingRemarkDir;
use std::any::Any;
use std::borrow::Cow;
use std::fs;
@@ -321,7 +321,6 @@ pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo
#[derive(Clone)]
pub struct CodegenContext<B: WriteBackendMethods> {
// Resources needed when running LTO
- pub backend: B,
pub prof: SelfProfilerRef,
pub lto: Lto,
pub save_temps: bool,
@@ -339,18 +338,17 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub msvc_imps_needed: bool,
pub is_pe_coff: bool,
pub target_can_use_split_dwarf: bool,
- pub target_pointer_width: u32,
pub target_arch: String,
- pub debuginfo: config::DebugInfo,
pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
- /// Number of cgus excluding the allocator/metadata modules
- pub total_cgus: usize,
/// Handler to use for diagnostics produced during codegen.
pub diag_emitter: SharedEmitter,
/// LLVM optimizations for which we want to print remarks.
pub remark: Passes,
+ /// Directory into which should the LLVM optimization remarks be written.
+ /// If `None`, they will be written to stderr.
+ pub remark_dir: Option<PathBuf>,
/// Worker thread number
pub worker: usize,
/// The incremental compilation session directory, or None if we are not
@@ -442,7 +440,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
target_cpu: String,
metadata: EncodedMetadata,
metadata_module: Option<CompiledModule>,
- total_cgus: usize,
) -> OngoingCodegen<B> {
let (coordinator_send, coordinator_receive) = channel();
let sess = tcx.sess;
@@ -470,7 +467,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
shared_emitter,
codegen_worker_send,
coordinator_receive,
- total_cgus,
sess.jobserver.clone(),
Arc::new(regular_config),
Arc::new(metadata_config),
@@ -498,8 +494,8 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
sess: &Session,
compiled_modules: &CompiledModules,
-) -> FxHashMap<WorkProductId, WorkProduct> {
- let mut work_products = FxHashMap::default();
+) -> FxIndexMap<WorkProductId, WorkProduct> {
+ let mut work_products = FxIndexMap::default();
if sess.opts.incremental.is_none() {
return work_products;
@@ -535,9 +531,16 @@ fn produce_final_output_artifacts(
let mut user_wants_objects = false;
// Produce final compile outputs.
- let copy_gracefully = |from: &Path, to: &Path| {
- if let Err(e) = fs::copy(from, to) {
- sess.emit_err(errors::CopyPath::new(from, to, e));
+ let copy_gracefully = |from: &Path, to: &OutFileName| match to {
+ OutFileName::Stdout => {
+ if let Err(e) = copy_to_stdout(from) {
+ sess.emit_err(errors::CopyPath::new(from, to.as_path(), e));
+ }
+ }
+ OutFileName::Real(path) => {
+ if let Err(e) = fs::copy(from, path) {
+ sess.emit_err(errors::CopyPath::new(from, path, e));
+ }
}
};
@@ -547,7 +550,12 @@ fn produce_final_output_artifacts(
// to copy `foo.0.x` to `foo.x`.
let module_name = Some(&compiled_modules.modules[0].name[..]);
let path = crate_output.temp_path(output_type, module_name);
- copy_gracefully(&path, &crate_output.path(output_type));
+ let output = crate_output.path(output_type);
+ if !output_type.is_text_output() && output.is_tty() {
+ sess.emit_err(errors::BinaryOutputToTty { shorthand: output_type.shorthand() });
+ } else {
+ copy_gracefully(&path, &output);
+ }
if !sess.opts.cg.save_temps && !keep_numbered {
// The user just wants `foo.x`, not `foo.#module-name#.x`.
ensure_removed(sess.diagnostic(), &path);
@@ -633,10 +641,10 @@ fn produce_final_output_artifacts(
// rlib.
let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe);
- let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1;
+ let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units().as_usize() > 1;
let keep_numbered_objects =
- needs_crate_object || (user_wants_objects && sess.codegen_units() > 1);
+ needs_crate_object || (user_wants_objects && sess.codegen_units().as_usize() > 1);
for module in compiled_modules.modules.iter() {
if let Some(ref path) = module.object {
@@ -674,7 +682,7 @@ fn produce_final_output_artifacts(
// These are used in linking steps and will be cleaned up afterward.
}
-pub enum WorkItem<B: WriteBackendMethods> {
+pub(crate) enum WorkItem<B: WriteBackendMethods> {
/// Optimize a newly codegened, totally unoptimized module.
Optimize(ModuleCodegen<B::Module>),
/// Copy the post-LTO artifacts from the incremental cache to the output
@@ -692,49 +700,57 @@ impl<B: WriteBackendMethods> WorkItem<B> {
}
}
- fn start_profiling<'a>(&self, cgcx: &'a CodegenContext<B>) -> TimingGuard<'a> {
- match *self {
- WorkItem::Optimize(ref m) => {
- cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*m.name)
- }
- WorkItem::CopyPostLtoArtifacts(ref m) => cgcx
- .prof
- .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*m.name),
- WorkItem::LTO(ref m) => {
- cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name())
- }
- }
- }
-
/// Generate a short description of this work item suitable for use as a thread name.
fn short_description(&self) -> String {
- // `pthread_setname()` on *nix is limited to 15 characters and longer names are ignored.
- // Use very short descriptions in this case to maximize the space available for the module name.
- // Windows does not have that limitation so use slightly more descriptive names there.
+ // `pthread_setname()` on *nix ignores anything beyond the first 15
+ // bytes. Use short descriptions to maximize the space available for
+ // the module name.
+ #[cfg(not(windows))]
+ fn desc(short: &str, _long: &str, name: &str) -> String {
+ // The short label is three bytes, and is followed by a space. That
+ // leaves 11 bytes for the CGU name. How we obtain those 11 bytes
+ // depends on the the CGU name form.
+ //
+ // - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part
+ // before the `-cgu.0` is the same for every CGU, so use the
+ // `cgu.0` part. The number suffix will be different for each
+ // CGU.
+ //
+ // - Incremental (normal), e.g. `2i52vvl2hco29us0`: use the whole
+ // name because each CGU will have a unique ASCII hash, and the
+ // first 11 bytes will be enough to identify it.
+ //
+ // - Incremental (with `-Zhuman-readable-cgu-names`), e.g.
+ // `regex.f10ba03eb5ec7975-re_builder.volatile`: use the whole
+ // name. The first 11 bytes won't be enough to uniquely identify
+ // it, but no obvious substring will, and this is a rarely used
+ // option so it doesn't matter much.
+ //
+ assert_eq!(short.len(), 3);
+ let name = if let Some(index) = name.find("-cgu.") {
+ &name[index + 1..] // +1 skips the leading '-'.
+ } else {
+ name
+ };
+ format!("{short} {name}")
+ }
+
+ // Windows has no thread name length limit, so use more descriptive names.
+ #[cfg(windows)]
+ fn desc(_short: &str, long: &str, name: &str) -> String {
+ format!("{long} {name}")
+ }
+
match self {
- WorkItem::Optimize(m) => {
- #[cfg(windows)]
- return format!("optimize module {}", m.name);
- #[cfg(not(windows))]
- return format!("opt {}", m.name);
- }
- WorkItem::CopyPostLtoArtifacts(m) => {
- #[cfg(windows)]
- return format!("copy LTO artifacts for {}", m.name);
- #[cfg(not(windows))]
- return format!("copy {}", m.name);
- }
- WorkItem::LTO(m) => {
- #[cfg(windows)]
- return format!("LTO module {}", m.name());
- #[cfg(not(windows))]
- return format!("LTO {}", m.name());
- }
+ WorkItem::Optimize(m) => desc("opt", "optimize module {}", &m.name),
+ WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for {}", &m.name),
+ WorkItem::LTO(m) => desc("lto", "LTO module {}", m.name()),
}
}
}
-enum WorkItemResult<B: WriteBackendMethods> {
+/// A result produced by the backend.
+pub(crate) enum WorkItemResult<B: WriteBackendMethods> {
Compiled(CompiledModule),
NeedsLink(ModuleCodegen<B::Module>),
NeedsFatLTO(FatLTOInput<B>),
@@ -746,21 +762,6 @@ pub enum FatLTOInput<B: WriteBackendMethods> {
InMemory(ModuleCodegen<B::Module>),
}
-fn execute_work_item<B: ExtraBackendMethods>(
- cgcx: &CodegenContext<B>,
- work_item: WorkItem<B>,
-) -> Result<WorkItemResult<B>, FatalError> {
- let module_config = cgcx.config(work_item.module_kind());
-
- match work_item {
- WorkItem::Optimize(module) => execute_optimize_work_item(cgcx, module, module_config),
- WorkItem::CopyPostLtoArtifacts(module) => {
- Ok(execute_copy_from_cache_work_item(cgcx, module, module_config))
- }
- WorkItem::LTO(module) => execute_lto_work_item(cgcx, module, module_config),
- }
-}
-
/// Actual LTO type we end up choosing based on multiple factors.
pub enum ComputedLtoType {
No,
@@ -941,38 +942,41 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
}
}
-pub enum Message<B: WriteBackendMethods> {
+/// Messages sent to the coordinator.
+pub(crate) enum Message<B: WriteBackendMethods> {
+ /// A jobserver token has become available. Sent from the jobserver helper
+ /// thread.
Token(io::Result<Acquired>),
- NeedsFatLTO {
- result: FatLTOInput<B>,
- worker_id: usize,
- },
- NeedsThinLTO {
- name: String,
- thin_buffer: B::ThinBuffer,
- worker_id: usize,
- },
- NeedsLink {
- module: ModuleCodegen<B::Module>,
- worker_id: usize,
- },
- Done {
- result: Result<CompiledModule, Option<WorkerFatalError>>,
- worker_id: usize,
- },
- CodegenDone {
- llvm_work_item: WorkItem<B>,
- cost: u64,
- },
+
+ /// The backend has finished processing a work item for a codegen unit.
+ /// Sent from a backend worker thread.
+ WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize },
+
+ /// The frontend has finished generating something (backend IR or a
+ /// post-LTO artifact) for a codegen unit, and it should be passed to the
+ /// backend. Sent from the main thread.
+ CodegenDone { llvm_work_item: WorkItem<B>, cost: u64 },
+
+ /// Similar to `CodegenDone`, but for reusing a pre-LTO artifact
+ /// Sent from the main thread.
AddImportOnlyModule {
module_data: SerializedModule<B::ModuleBuffer>,
work_product: WorkProduct,
},
+
+ /// The frontend has finished generating everything for all codegen units.
+ /// Sent from the main thread.
CodegenComplete,
- CodegenItem,
+
+ /// Some normal-ish compiler error occurred, and codegen should be wound
+ /// down. Sent from the main thread.
CodegenAborted,
}
+/// A message sent from the coordinator thread to the main thread telling it to
+/// process another codegen unit.
+pub struct CguMessage;
+
type DiagnosticArgName<'source> = Cow<'source, str>;
struct Diagnostic {
@@ -994,9 +998,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
shared_emitter: SharedEmitter,
- codegen_worker_send: Sender<Message<B>>,
+ codegen_worker_send: Sender<CguMessage>,
coordinator_receive: Receiver<Box<dyn Any + Send>>,
- total_cgus: usize,
jobserver: Client,
regular_config: Arc<ModuleConfig>,
metadata_config: Arc<ModuleConfig>,
@@ -1063,8 +1066,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
tcx.backend_optimization_level(())
};
let backend_features = tcx.global_backend_features(());
+
+ let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir {
+ let result = fs::create_dir_all(dir).and_then(|_| dir.canonicalize());
+ match result {
+ Ok(dir) => Some(dir),
+ Err(error) => sess.emit_fatal(ErrorCreatingRemarkDir { error }),
+ }
+ } else {
+ None
+ };
+
let cgcx = CodegenContext::<B> {
- backend: backend.clone(),
crate_types: sess.crate_types().to_vec(),
each_linked_rlib_for_lto,
lto: sess.lto(),
@@ -1075,6 +1088,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
prof: sess.prof.clone(),
exported_symbols,
remark: sess.opts.cg.remark.clone(),
+ remark_dir,
worker: 0,
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
@@ -1085,13 +1099,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
metadata_module_config: metadata_config,
allocator_module_config: allocator_config,
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
- total_cgus,
msvc_imps_needed: msvc_imps_needed(tcx),
is_pe_coff: tcx.sess.target.is_like_windows,
target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(),
- target_pointer_width: tcx.sess.target.pointer_width,
target_arch: tcx.sess.target.arch.to_string(),
- debuginfo: tcx.sess.opts.debuginfo,
split_debuginfo: tcx.sess.split_debuginfo(),
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
};
@@ -1253,10 +1264,19 @@ fn start_executing_work<B: ExtraBackendMethods>(
let mut needs_thin_lto = Vec::new();
let mut lto_import_only_modules = Vec::new();
let mut started_lto = false;
- let mut codegen_aborted = false;
- // This flag tracks whether all items have gone through codegens
- let mut codegen_done = false;
+ /// Possible state transitions:
+ /// - Ongoing -> Completed
+ /// - Ongoing -> Aborted
+ /// - Completed -> Aborted
+ #[derive(Debug, PartialEq)]
+ enum CodegenState {
+ Ongoing,
+ Completed,
+ Aborted,
+ }
+ use CodegenState::*;
+ let mut codegen_state = Ongoing;
// This is the queue of LLVM work items that still need processing.
let mut work_items = Vec::<(WorkItem<B>, u64)>::new();
@@ -1276,10 +1296,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
// wait for all existing work to finish, so many of the conditions here
// only apply if codegen hasn't been aborted as they represent pending
// work to be done.
- while !codegen_done
+ while codegen_state == Ongoing
|| running > 0
|| main_thread_worker_state == MainThreadWorkerState::LLVMing
- || (!codegen_aborted
+ || (codegen_state == Completed
&& !(work_items.is_empty()
&& needs_fat_lto.is_empty()
&& needs_thin_lto.is_empty()
@@ -1289,7 +1309,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
// While there are still CGUs to be codegened, the coordinator has
// to decide how to utilize the compiler processes implicit Token:
// For codegenning more CGU or for running them through LLVM.
- if !codegen_done {
+ if codegen_state == Ongoing {
if main_thread_worker_state == MainThreadWorkerState::Idle {
// Compute the number of workers that will be running once we've taken as many
// items from the work queue as we can, plus one for the main thread. It's not
@@ -1302,9 +1322,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
let anticipated_running = running + additional_running + 1;
if !queue_full_enough(work_items.len(), anticipated_running) {
- // The queue is not full enough, codegen more items:
- if codegen_worker_send.send(Message::CodegenItem).is_err() {
- panic!("Could not send Message::CodegenItem to main thread")
+ // The queue is not full enough, process more codegen units:
+ if codegen_worker_send.send(CguMessage).is_err() {
+ panic!("Could not send CguMessage to main thread")
}
main_thread_worker_state = MainThreadWorkerState::Codegenning;
} else {
@@ -1326,10 +1346,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
spawn_work(cgcx, item);
}
}
- } else if codegen_aborted {
- // don't queue up any more work if codegen was aborted, we're
- // just waiting for our existing children to finish
- } else {
+ } else if codegen_state == Completed {
// If we've finished everything related to normal codegen
// then it must be the case that we've got some LTO work to do.
// Perform the serial work here of figuring out what we're
@@ -1396,11 +1413,15 @@ fn start_executing_work<B: ExtraBackendMethods>(
// Already making good use of that token
}
}
+ } else {
+ // Don't queue up any more work if codegen was aborted, we're
+ // just waiting for our existing children to finish.
+ assert!(codegen_state == Aborted);
}
// Spin up what work we can, only doing this while we've got available
// parallelism slots and work left to spawn.
- while !codegen_aborted && !work_items.is_empty() && running < tokens.len() {
+ while codegen_state != Aborted && !work_items.is_empty() && running < tokens.len() {
let (item, _) = work_items.pop().unwrap();
maybe_start_llvm_timer(prof, cgcx.config(item.module_kind()), &mut llvm_start_time);
@@ -1452,8 +1473,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
Err(e) => {
let msg = &format!("failed to acquire jobserver token: {}", e);
shared_emitter.fatal(msg);
- codegen_done = true;
- codegen_aborted = true;
+ codegen_state = Aborted;
}
}
}
@@ -1481,7 +1501,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
}
Message::CodegenComplete => {
- codegen_done = true;
+ if codegen_state != Aborted {
+ codegen_state = Completed;
+ }
assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
main_thread_worker_state = MainThreadWorkerState::Idle;
}
@@ -1493,58 +1515,59 @@ fn start_executing_work<B: ExtraBackendMethods>(
// then conditions above will ensure no more work is spawned but
// we'll keep executing this loop until `running` hits 0.
Message::CodegenAborted => {
- codegen_done = true;
- codegen_aborted = true;
+ codegen_state = Aborted;
}
- Message::Done { result: Ok(compiled_module), worker_id } => {
+
+ Message::WorkItem { result, worker_id } => {
free_worker(worker_id);
- match compiled_module.kind {
- ModuleKind::Regular => {
- compiled_modules.push(compiled_module);
+
+ match result {
+ Ok(WorkItemResult::Compiled(compiled_module)) => {
+ match compiled_module.kind {
+ ModuleKind::Regular => {
+ compiled_modules.push(compiled_module);
+ }
+ ModuleKind::Allocator => {
+ assert!(compiled_allocator_module.is_none());
+ compiled_allocator_module = Some(compiled_module);
+ }
+ ModuleKind::Metadata => bug!("Should be handled separately"),
+ }
+ }
+ Ok(WorkItemResult::NeedsLink(module)) => {
+ needs_link.push(module);
+ }
+ Ok(WorkItemResult::NeedsFatLTO(fat_lto_input)) => {
+ assert!(!started_lto);
+ needs_fat_lto.push(fat_lto_input);
+ }
+ Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) => {
+ assert!(!started_lto);
+ needs_thin_lto.push((name, thin_buffer));
+ }
+ Err(Some(WorkerFatalError)) => {
+ // Like `CodegenAborted`, wait for remaining work to finish.
+ codegen_state = Aborted;
}
- ModuleKind::Allocator => {
- assert!(compiled_allocator_module.is_none());
- compiled_allocator_module = Some(compiled_module);
+ Err(None) => {
+ // If the thread failed that means it panicked, so
+ // we abort immediately.
+ bug!("worker thread panicked");
}
- ModuleKind::Metadata => bug!("Should be handled separately"),
}
}
- Message::NeedsLink { module, worker_id } => {
- free_worker(worker_id);
- needs_link.push(module);
- }
- Message::NeedsFatLTO { result, worker_id } => {
- assert!(!started_lto);
- free_worker(worker_id);
- needs_fat_lto.push(result);
- }
- Message::NeedsThinLTO { name, thin_buffer, worker_id } => {
- assert!(!started_lto);
- free_worker(worker_id);
- needs_thin_lto.push((name, thin_buffer));
- }
+
Message::AddImportOnlyModule { module_data, work_product } => {
assert!(!started_lto);
- assert!(!codegen_done);
+ assert_eq!(codegen_state, Ongoing);
assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
lto_import_only_modules.push((module_data, work_product));
main_thread_worker_state = MainThreadWorkerState::Idle;
}
- // If the thread failed that means it panicked, so we abort immediately.
- Message::Done { result: Err(None), worker_id: _ } => {
- bug!("worker thread panicked");
- }
- Message::Done { result: Err(Some(WorkerFatalError)), worker_id } => {
- // Similar to CodegenAborted, wait for remaining work to finish.
- free_worker(worker_id);
- codegen_done = true;
- codegen_aborted = true;
- }
- Message::CodegenItem => bug!("the coordinator should not receive codegen requests"),
}
}
- if codegen_aborted {
+ if codegen_state == Aborted {
return Err(());
}
@@ -1659,22 +1682,11 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
fn drop(&mut self) {
let worker_id = self.worker_id;
let msg = match self.result.take() {
- Some(Ok(WorkItemResult::Compiled(m))) => {
- Message::Done::<B> { result: Ok(m), worker_id }
- }
- Some(Ok(WorkItemResult::NeedsLink(m))) => {
- Message::NeedsLink::<B> { module: m, worker_id }
- }
- Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
- Message::NeedsFatLTO::<B> { result: m, worker_id }
- }
- Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => {
- Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id }
- }
+ Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result), worker_id },
Some(Err(FatalError)) => {
- Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id }
+ Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)), worker_id }
}
- None => Message::Done::<B> { result: Err(None), worker_id },
+ None => Message::WorkItem::<B> { result: Err(None), worker_id },
};
drop(self.coordinator_send.send(Box::new(msg)));
}
@@ -1693,8 +1705,27 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
// as a diagnostic was already sent off to the main thread - just
// surface that there was an error in this worker.
bomb.result = {
- let _prof_timer = work.start_profiling(&cgcx);
- Some(execute_work_item(&cgcx, work))
+ let module_config = cgcx.config(work.module_kind());
+
+ Some(match work {
+ WorkItem::Optimize(m) => {
+ let _timer =
+ cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*m.name);
+ execute_optimize_work_item(&cgcx, m, module_config)
+ }
+ WorkItem::CopyPostLtoArtifacts(m) => {
+ let _timer = cgcx.prof.generic_activity_with_arg(
+ "codegen_copy_artifacts_from_incr_cache",
+ &*m.name,
+ );
+ Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config))
+ }
+ WorkItem::LTO(m) => {
+ let _timer =
+ cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name());
+ execute_lto_work_item(&cgcx, m, module_config)
+ }
+ })
};
})
.expect("failed to spawn thread");
@@ -1800,7 +1831,7 @@ impl SharedEmitterMain {
handler.emit_diagnostic(&mut d);
}
Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
- let msg = msg.strip_prefix("error: ").unwrap_or(&msg);
+ let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
let mut err = match level {
Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
@@ -1878,14 +1909,14 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> {
pub metadata: EncodedMetadata,
pub metadata_module: Option<CompiledModule>,
pub crate_info: CrateInfo,
- pub codegen_worker_receive: Receiver<Message<B>>,
+ pub codegen_worker_receive: Receiver<CguMessage>,
pub shared_emitter_main: SharedEmitterMain,
pub output_filenames: Arc<OutputFilenames>,
pub coordinator: Coordinator<B>,
}
impl<B: ExtraBackendMethods> OngoingCodegen<B> {
- pub fn join(self, sess: &Session) -> (CodegenResults, FxHashMap<WorkProductId, WorkProduct>) {
+ pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
let _timer = sess.timer("finish_ongoing_codegen");
self.shared_emitter_main.check(sess, true);
@@ -1910,7 +1941,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
// FIXME: time_llvm_passes support - does this use a global context or
// something?
- if sess.codegen_units() == 1 && sess.opts.unstable_opts.time_llvm_passes {
+ if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes {
self.backend.print_pass_timings()
}
@@ -1952,10 +1983,9 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
pub fn wait_for_signal_to_codegen_item(&self) {
match self.codegen_worker_receive.recv() {
- Ok(Message::CodegenItem) => {
- // Nothing to do
+ Ok(CguMessage) => {
+ // Ok to proceed.
}
- Ok(_) => panic!("unexpected message"),
Err(_) => {
// One of the LLVM threads must have panicked, fall through so
// error handling can be reached.