diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
commit | dc0db358abe19481e475e10c32149b53370f1a1c (patch) | |
tree | ab8ce99c4b255ce46f99ef402c27916055b899ee /compiler/rustc_codegen_ssa/src | |
parent | Releasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
29 files changed, 1070 insertions, 1080 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. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index ab7dd1ba8..9133601ec 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -199,7 +199,7 @@ fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>( cx.scalar_pair_element_backend_type( cx.layout_of(match kind { // vtable is the second field of `*mut dyn Trait` - ty::Dyn => cx.tcx().mk_mut_ptr(target), + ty::Dyn => Ty::new_mut_ptr(cx.tcx(), target), // vtable is the second field of `dyn* Trait` ty::DynStar => target, }), @@ -295,7 +295,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let (base, info) = match bx.load_operand(src).val { OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)), OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None), - OperandValue::Ref(..) => bug!(), + OperandValue::Ref(..) | OperandValue::ZeroSized => bug!(), }; OperandValue::Pair(base, info).store(bx, dst); } @@ -357,6 +357,13 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } +// Returns `true` if this session's target will use native wasm +// exceptions. This means that the VM does the unwinding for +// us +pub fn wants_wasm_eh(sess: &Session) -> bool { + sess.target.is_like_wasm && sess.target.os != "emscripten" +} + /// Returns `true` if this session's target will use SEH-based unwinding. /// /// This is only true for MSVC targets, and even then the 64-bit MSVC target @@ -366,6 +373,13 @@ pub fn wants_msvc_seh(sess: &Session) -> bool { sess.target.is_like_msvc } +/// Returns `true` if this session's target requires the new exception +/// handling LLVM IR instructions (catchpad / cleanuppad / ... instead +/// of landingpad) +pub fn wants_new_eh_instructions(sess: &Session) -> bool { + wants_wasm_eh(sess) || wants_msvc_seh(sess) +} + pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, dst: Bx::Value, @@ -380,7 +394,19 @@ pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return; } - bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); + if flags == MemFlags::empty() + && let Some(bty) = bx.cx().scalar_copy_backend_type(layout) + { + // I look forward to only supporting opaque pointers + let pty = bx.type_ptr_to(bty); + let src = bx.pointercast(src, pty); + let dst = bx.pointercast(dst, pty); + + let temp = bx.load(bty, src, src_align); + bx.store(temp, dst, dst_align); + } else { + bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); + } } pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( @@ -568,7 +594,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( ) -> OngoingCodegen<B> { // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { - let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None, 1); + let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None); ongoing_codegen.codegen_finished(tcx); @@ -619,14 +645,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>( }) }); - let ongoing_codegen = start_async_codegen( - backend.clone(), - tcx, - target_cpu, - metadata, - metadata_module, - codegen_units.len(), - ); + let ongoing_codegen = + start_async_codegen(backend.clone(), tcx, target_cpu, metadata, metadata_module); // Codegen an allocator shim, if necessary. if let Some(kind) = allocator_kind_for_codegen(tcx) { diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index e1abb73a5..5a6807599 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -132,7 +132,7 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // all shifts). For 32- and 64-bit types, this matches the semantics // of Java. (See related discussion on #1877 and #10183.) -pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, lhs: Bx::Value, rhs: Bx::Value, @@ -143,7 +143,7 @@ pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.shl(lhs, rhs) } -pub fn build_unchecked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, lhs_t: Ty<'tcx>, lhs: Bx::Value, diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs deleted file mode 100644 index 1791ce4b3..000000000 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs +++ /dev/null @@ -1,89 +0,0 @@ -use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; - -/// Must match the layout of `LLVMRustCounterKind`. -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub enum CounterKind { - Zero = 0, - CounterValueReference = 1, - Expression = 2, -} - -/// A reference to an instance of an abstract "counter" that will yield a value in a coverage -/// report. Note that `id` has different interpretations, depending on the `kind`: -/// * For `CounterKind::Zero`, `id` is assumed to be `0` -/// * For `CounterKind::CounterValueReference`, `id` matches the `counter_id` of the injected -/// instrumentation counter (the `index` argument to the LLVM intrinsic -/// `instrprof.increment()`) -/// * For `CounterKind::Expression`, `id` is the index into the coverage map's array of -/// counter expressions. -/// -/// Corresponds to struct `llvm::coverage::Counter`. -/// -/// Must match the layout of `LLVMRustCounter`. -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct Counter { - // Important: The layout (order and types of fields) must match its C++ counterpart. - pub kind: CounterKind, - id: u32, -} - -impl Counter { - /// Constructs a new `Counter` of kind `Zero`. For this `CounterKind`, the - /// `id` is not used. - pub fn zero() -> Self { - Self { kind: CounterKind::Zero, id: 0 } - } - - /// Constructs a new `Counter` of kind `CounterValueReference`, and converts - /// the given 1-based counter_id to the required 0-based equivalent for - /// the `Counter` encoding. - pub fn counter_value_reference(counter_id: CounterValueReference) -> Self { - Self { kind: CounterKind::CounterValueReference, id: counter_id.zero_based_index() } - } - - /// Constructs a new `Counter` of kind `Expression`. - pub fn expression(mapped_expression_index: MappedExpressionIndex) -> Self { - Self { kind: CounterKind::Expression, id: mapped_expression_index.into() } - } - - /// Returns true if the `Counter` kind is `Zero`. - pub fn is_zero(&self) -> bool { - matches!(self.kind, CounterKind::Zero) - } - - /// An explicitly-named function to get the ID value, making it more obvious - /// that the stored value is now 0-based. - pub fn zero_based_id(&self) -> u32 { - debug_assert!(!self.is_zero(), "`id` is undefined for CounterKind::Zero"); - self.id - } -} - -/// Corresponds to enum `llvm::coverage::CounterExpression::ExprKind`. -/// -/// Must match the layout of `LLVMRustCounterExprKind`. -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub enum ExprKind { - Subtract = 0, - Add = 1, -} - -/// Corresponds to struct `llvm::coverage::CounterExpression`. -/// -/// Must match the layout of `LLVMRustCounterExpression`. -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct CounterExpression { - pub kind: ExprKind, - pub lhs: Counter, - pub rhs: Counter, -} - -impl CounterExpression { - pub fn new(lhs: Counter, kind: ExprKind, rhs: Counter) -> Self { - Self { kind, lhs, rhs } - } -} diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs deleted file mode 100644 index e4da3b8de..000000000 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ /dev/null @@ -1,347 +0,0 @@ -pub use super::ffi::*; - -use rustc_index::{IndexSlice, IndexVec}; -use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, - InjectedExpressionIndex, MappedExpressionIndex, Op, -}; -use rustc_middle::ty::Instance; -use rustc_middle::ty::TyCtxt; - -#[derive(Clone, Debug, PartialEq)] -pub struct Expression { - lhs: ExpressionOperandId, - op: Op, - rhs: ExpressionOperandId, - region: Option<CodeRegion>, -} - -/// Collects all of the coverage regions associated with (a) injected counters, (b) counter -/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero), -/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they -/// can both be operands in an expression. This struct also stores the `function_source_hash`, -/// computed during instrumentation, and forwarded with counters. -/// -/// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap -/// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter -/// or expression), but the line or lines in the gap region are not executable (such as lines with -/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count -/// for a gap area is only used as the line execution count if there are no other regions on a -/// line." -#[derive(Debug)] -pub struct FunctionCoverage<'tcx> { - instance: Instance<'tcx>, - source_hash: u64, - is_used: bool, - counters: IndexVec<CounterValueReference, Option<CodeRegion>>, - expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>, - unreachable_regions: Vec<CodeRegion>, -} - -impl<'tcx> FunctionCoverage<'tcx> { - /// Creates a new set of coverage data for a used (called) function. - pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { - Self::create(tcx, instance, true) - } - - /// Creates a new set of coverage data for an unused (never called) function. - pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { - Self::create(tcx, instance, false) - } - - fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self { - let coverageinfo = tcx.coverageinfo(instance.def); - debug!( - "FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}", - instance, coverageinfo, is_used - ); - Self { - instance, - source_hash: 0, // will be set with the first `add_counter()` - is_used, - counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), - expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), - unreachable_regions: Vec::new(), - } - } - - /// Returns true for a used (called) function, and false for an unused function. - pub fn is_used(&self) -> bool { - self.is_used - } - - /// Sets the function source hash value. If called multiple times for the same function, all - /// calls should have the same hash value. - pub fn set_function_source_hash(&mut self, source_hash: u64) { - if self.source_hash == 0 { - self.source_hash = source_hash; - } else { - debug_assert_eq!(source_hash, self.source_hash); - } - } - - /// Adds a code region to be counted by an injected counter intrinsic. - pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { - if let Some(previous_region) = self.counters[id].replace(region.clone()) { - assert_eq!(previous_region, region, "add_counter: code region for id changed"); - } - } - - /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other - /// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression - /// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in - /// any order, and expressions can still be assigned contiguous (though descending) IDs, without - /// knowing what the last counter ID will be. - /// - /// When storing the expression data in the `expressions` vector in the `FunctionCoverage` - /// struct, its vector index is computed, from the given expression ID, by subtracting from - /// `u32::MAX`. - /// - /// Since the expression operands (`lhs` and `rhs`) can reference either counters or - /// expressions, an operand that references an expression also uses its original ID, descending - /// from `u32::MAX`. Theses operands are translated only during code generation, after all - /// counters and expressions have been added. - pub fn add_counter_expression( - &mut self, - expression_id: InjectedExpressionId, - lhs: ExpressionOperandId, - op: Op, - rhs: ExpressionOperandId, - region: Option<CodeRegion>, - ) { - debug!( - "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", - expression_id, lhs, op, rhs, region - ); - let expression_index = self.expression_index(u32::from(expression_id)); - debug_assert!( - expression_index.as_usize() < self.expressions.len(), - "expression_index {} is out of range for expressions.len() = {} - for {:?}", - expression_index.as_usize(), - self.expressions.len(), - self, - ); - if let Some(previous_expression) = self.expressions[expression_index].replace(Expression { - lhs, - op, - rhs, - region: region.clone(), - }) { - assert_eq!( - previous_expression, - Expression { lhs, op, rhs, region }, - "add_counter_expression: expression for id changed" - ); - } - } - - /// Add a region that will be marked as "unreachable", with a constant "zero counter". - pub fn add_unreachable_region(&mut self, region: CodeRegion) { - self.unreachable_regions.push(region) - } - - /// Return the source hash, generated from the HIR node structure, and used to indicate whether - /// or not the source code structure changed between different compilations. - pub fn source_hash(&self) -> u64 { - self.source_hash - } - - /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their - /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create - /// `CounterMappingRegion`s. - pub fn get_expressions_and_counter_regions( - &self, - ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) { - assert!( - self.source_hash != 0 || !self.is_used, - "No counters provided the source_hash for used function: {:?}", - self.instance - ); - - let counter_regions = self.counter_regions(); - let (counter_expressions, expression_regions) = self.expressions_with_regions(); - let unreachable_regions = self.unreachable_regions(); - - let counter_regions = - counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions)); - (counter_expressions, counter_regions) - } - - fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> { - self.counters.iter_enumerated().filter_map(|(index, entry)| { - // Option::map() will return None to filter out missing counters. This may happen - // if, for example, a MIR-instrumented counter is removed during an optimization. - entry.as_ref().map(|region| (Counter::counter_value_reference(index), region)) - }) - } - - fn expressions_with_regions( - &self, - ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) { - let mut counter_expressions = Vec::with_capacity(self.expressions.len()); - let mut expression_regions = Vec::with_capacity(self.expressions.len()); - let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len()); - - // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or - // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type - // and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range - // of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value - // matches the injected counter index); and any other value is converted into a - // `CounterKind::Expression` with the expression's `new_index`. - // - // Expressions will be returned from this function in a sequential vector (array) of - // `CounterExpression`, so the expression IDs must be mapped from their original, - // potentially sparse set of indexes, originally in reverse order from `u32::MAX`. - // - // An `Expression` as an operand will have already been encountered as an `Expression` with - // operands, so its new_index will already have been generated (as a 1-up index value). - // (If an `Expression` as an operand does not have a corresponding new_index, it was - // probably optimized out, after the expression was injected into the MIR, so it will - // get a `CounterKind::Zero` instead.) - // - // In other words, an `Expression`s at any given index can include other expressions as - // operands, but expression operands can only come from the subset of expressions having - // `expression_index`s lower than the referencing `Expression`. Therefore, it is - // reasonable to look up the new index of an expression operand while the `new_indexes` - // vector is only complete up to the current `ExpressionIndex`. - let id_to_counter = |new_indexes: &IndexSlice< - InjectedExpressionIndex, - Option<MappedExpressionIndex>, - >, - id: ExpressionOperandId| { - if id == ExpressionOperandId::ZERO { - Some(Counter::zero()) - } else if id.index() < self.counters.len() { - debug_assert!( - id.index() > 0, - "ExpressionOperandId indexes for counters are 1-based, but this id={}", - id.index() - ); - // Note: Some codegen-injected Counters may be only referenced by `Expression`s, - // and may not have their own `CodeRegion`s, - let index = CounterValueReference::from(id.index()); - // Note, the conversion to LLVM `Counter` adjusts the index to be zero-based. - Some(Counter::counter_value_reference(index)) - } else { - let index = self.expression_index(u32::from(id)); - self.expressions - .get(index) - .expect("expression id is out of range") - .as_ref() - // If an expression was optimized out, assume it would have produced a count - // of zero. This ensures that expressions dependent on optimized-out - // expressions are still valid. - .map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression)) - } - }; - - for (original_index, expression) in - self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { - // Option::map() will return None to filter out missing expressions. This may happen - // if, for example, a MIR-instrumented expression is removed during an optimization. - entry.as_ref().map(|expression| (original_index, expression)) - }) - { - let optional_region = &expression.region; - let Expression { lhs, op, rhs, .. } = *expression; - - if let Some(Some((lhs_counter, mut rhs_counter))) = id_to_counter(&new_indexes, lhs) - .map(|lhs_counter| { - id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) - }) - { - if lhs_counter.is_zero() && op.is_subtract() { - // The left side of a subtraction was probably optimized out. As an example, - // a branch condition might be evaluated as a constant expression, and the - // branch could be removed, dropping unused counters in the process. - // - // Since counters are unsigned, we must assume the result of the expression - // can be no more and no less than zero. An expression known to evaluate to zero - // does not need to be added to the coverage map. - // - // Coverage test `loops_branches.rs` includes multiple variations of branches - // based on constant conditional (literal `true` or `false`), and demonstrates - // that the expected counts are still correct. - debug!( - "Expression subtracts from zero (assume unreachable): \ - original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", - original_index, lhs, op, rhs, optional_region, - ); - rhs_counter = Counter::zero(); - } - debug_assert!( - lhs_counter.is_zero() - // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` - || ((lhs_counter.zero_based_id() as usize) - <= usize::max(self.counters.len(), self.expressions.len())), - "lhs id={} > both counters.len()={} and expressions.len()={} - ({:?} {:?} {:?})", - lhs_counter.zero_based_id(), - self.counters.len(), - self.expressions.len(), - lhs_counter, - op, - rhs_counter, - ); - - debug_assert!( - rhs_counter.is_zero() - // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16` - || ((rhs_counter.zero_based_id() as usize) - <= usize::max(self.counters.len(), self.expressions.len())), - "rhs id={} > both counters.len()={} and expressions.len()={} - ({:?} {:?} {:?})", - rhs_counter.zero_based_id(), - self.counters.len(), - self.expressions.len(), - lhs_counter, - op, - rhs_counter, - ); - - // Both operands exist. `Expression` operands exist in `self.expressions` and have - // been assigned a `new_index`. - let mapped_expression_index = - MappedExpressionIndex::from(counter_expressions.len()); - let expression = CounterExpression::new( - lhs_counter, - match op { - Op::Add => ExprKind::Add, - Op::Subtract => ExprKind::Subtract, - }, - rhs_counter, - ); - debug!( - "Adding expression {:?} = {:?}, region: {:?}", - mapped_expression_index, expression, optional_region - ); - counter_expressions.push(expression); - new_indexes[original_index] = Some(mapped_expression_index); - if let Some(region) = optional_region { - expression_regions.push((Counter::expression(mapped_expression_index), region)); - } - } else { - bug!( - "expression has one or more missing operands \ - original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", - original_index, - lhs, - op, - rhs, - optional_region, - ); - } - } - (counter_expressions, expression_regions.into_iter()) - } - - fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> { - self.unreachable_regions.iter().map(|region| (Counter::zero(), region)) - } - - fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex { - debug_assert!(id_descending_from_max >= self.counters.len() as u32); - InjectedExpressionIndex::from(u32::MAX - id_descending_from_max) - } -} diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs deleted file mode 100644 index 569fd3f1a..000000000 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod ffi; -pub mod map; diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 6297f9134..e91f7b86e 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -93,8 +93,7 @@ fn push_debuginfo_type_name<'tcx>( Err(e) => { // Computing the layout can still fail here, e.g. if the target architecture // cannot represent the type. See https://github.com/rust-lang/rust/issues/94961. - // FIXME: migrate once `rustc_middle::mir::interpret::InterpError` is translatable. - tcx.sess.fatal(format!("{}", e)); + tcx.sess.emit_fatal(e.into_diagnostic()); } } } else { diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index cf4893b82..056b4abd2 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -83,6 +83,12 @@ impl IntoDiagnosticArg for DebugArgPath<'_> { } #[derive(Diagnostic)] +#[diag(codegen_ssa_binary_output_to_tty)] +pub struct BinaryOutputToTty { + pub shorthand: &'static str, +} + +#[derive(Diagnostic)] #[diag(codegen_ssa_ignoring_emit_path)] pub struct IgnoringEmitPath { pub extension: String, @@ -336,7 +342,7 @@ pub struct LinkingFailed<'a> { pub linker_path: &'a PathBuf, pub exit_status: ExitStatus, pub command: &'a Command, - pub escaped_output: &'a str, + pub escaped_output: String, } impl IntoDiagnostic<'_> for LinkingFailed<'_> { @@ -345,11 +351,13 @@ impl IntoDiagnostic<'_> for LinkingFailed<'_> { diag.set_arg("linker_path", format!("{}", self.linker_path.display())); diag.set_arg("exit_status", format!("{}", self.exit_status)); - diag.note(format!("{:?}", self.command)).note(self.escaped_output); + let contains_undefined_ref = self.escaped_output.contains("undefined reference to"); + + diag.note(format!("{:?}", self.command)).note(self.escaped_output.to_string()); // Trying to match an error from OS linkers // which by now we have no way to translate. - if self.escaped_output.contains("undefined reference to") { + if contains_undefined_ref { diag.note(fluent::codegen_ssa_extern_funcs_not_found) .note(fluent::codegen_ssa_specify_libraries_to_link) .note(fluent::codegen_ssa_use_cargo_directive); @@ -1015,3 +1023,9 @@ pub struct TargetFeatureSafeTrait { #[label(codegen_ssa_label_def)] pub def: Span, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_error_creating_remark_dir)] +pub struct ErrorCreatingRemarkDir { + pub error: std::io::Error, +} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 31854c7f4..be4c81638 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -4,6 +4,7 @@ #![feature(if_let_guard)] #![feature(int_roundings)] #![feature(let_chains)] +#![feature(negative_impls)] #![feature(never_type)] #![feature(strict_provenance)] #![feature(try_blocks)] @@ -47,7 +48,6 @@ pub mod back; pub mod base; pub mod codegen_attrs; pub mod common; -pub mod coverageinfo; pub mod debuginfo; pub mod errors; pub mod glue; diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 3f0b64b11..9d1b3ce82 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,5 +1,5 @@ use super::operand::OperandRef; -use super::operand::OperandValue::{Immediate, Pair, Ref}; +use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized}; use super::place::PlaceRef; use super::{CachedLlbb, FunctionCx, LocalRef}; @@ -79,8 +79,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { lltarget = fx.landing_pad_for(target); } if is_cleanupret { - // MSVC cross-funclet jump - need a trampoline - debug_assert!(base::wants_msvc_seh(fx.cx.tcx().sess)); + // Cross-funclet jump - need a trampoline + debug_assert!(base::wants_new_eh_instructions(fx.cx.tcx().sess)); debug!("llbb_with_cleanup: creating cleanup trampoline for {:?}", target); let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target); let trampoline_llbb = Bx::append_block(fx.cx, fx.llfn, name); @@ -177,9 +177,16 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, mir::UnwindAction::Terminate => { - if fx.mir[self.bb].is_cleanup && base::wants_msvc_seh(fx.cx.tcx().sess) { - // SEH will abort automatically if an exception tries to + if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) { + // MSVC SEH will abort automatically if an exception tries to // propagate out from cleanup. + + // FIXME(@mirkootter): For wasm, we currently do not support terminate during + // cleanup, because this requires a few more changes: The current code + // caches the `terminate_block` for each function; funclet based code - however - + // requires a different terminate_block for each funclet + // Until this is implemented, we just do not unwind inside cleanup blocks + None } else { Some(fx.terminate_block()) @@ -427,6 +434,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { assert_eq!(align, op.layout.align.abi, "return place is unaligned!"); llval } + ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"), }; let ty = bx.cast_backend_type(cast_ty); let addr = bx.pointercast(llslot, bx.type_ptr_to(ty)); @@ -615,7 +623,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { AssertKind::MisalignedPointerDereference { ref required, ref found } => { let required = self.codegen_operand(bx, required).immediate(); let found = self.codegen_operand(bx, found).immediate(); - // It's `fn panic_bounds_check(index: usize, len: usize)`, + // It's `fn panic_misaligned_pointer_dereference(required: usize, found: usize)`, // and `#[track_caller]` adds an implicit third argument. (LangItem::PanicMisalignedPointerDereference, vec![required, found, location]) } @@ -862,13 +870,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // promotes any complex rvalues to constants. if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") { if let mir::Operand::Constant(constant) = arg { - let c = self.eval_mir_constant(constant); - let (llval, ty) = self.simd_shuffle_indices( - &bx, - constant.span, - self.monomorphize(constant.ty()), - c, - ); + let (llval, ty) = self.simd_shuffle_indices(&bx, constant); return OperandRef { val: Immediate(llval), layout: bx.layout_of(ty), @@ -1279,7 +1281,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { destination, target, unwind, - from_hir_call: _, + call_source: _, fn_span, } => self.codegen_call_terminator( helper, @@ -1386,6 +1388,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (llval, align, true) } } + ZeroSized => match arg.mode { + PassMode::Indirect { .. } => { + // Though `extern "Rust"` doesn't pass ZSTs, some ABIs pass + // a pointer for `repr(C)` structs even when empty, so get + // one from an `alloca` (which can be left uninitialized). + let scratch = PlaceRef::alloca(bx, arg.layout); + (scratch.llval, scratch.align, true) + } + _ => bug!("ZST {op:?} wasn't ignored, but was passed with abi {arg:?}"), + }, }; if by_ref && !arg.is_indirect() { @@ -1493,9 +1505,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(slot) = self.personality_slot { slot } else { - let layout = cx.layout_of( - cx.tcx().mk_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]), - ); + let layout = cx.layout_of(Ty::new_tup( + cx.tcx(), + &[Ty::new_mut_ptr(cx.tcx(), cx.tcx().types.u8), cx.tcx().types.i32], + )); let slot = PlaceRef::alloca(bx, layout); self.personality_slot = Some(slot); slot @@ -1517,7 +1530,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // FIXME(eddyb) rename this to `eh_pad_for_uncached`. fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock { let llbb = self.llbb(bb); - if base::wants_msvc_seh(self.cx.sess()) { + if base::wants_new_eh_instructions(self.cx.sess()) { let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb)); let mut cleanup_bx = Bx::build(self.cx, cleanup_bb); let funclet = cleanup_bx.cleanup_pad(None, &[]); @@ -1576,6 +1589,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // } catch (...) { // bar(); // } + // + // which creates an IR snippet like + // + // cs_terminate: + // %cs = catchswitch within none [%cp_terminate] unwind to caller + // cp_terminate: + // %cp = catchpad within %cs [null, i32 64, null] + // ... + llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate"); let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); @@ -1718,7 +1740,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { IndirectOperand(tmp, index) => { let op = bx.load_operand(tmp); tmp.storage_dead(bx); - self.locals[index] = LocalRef::Operand(op); + self.overwrite_local(index, LocalRef::Operand(op)); self.debug_introduce_local(bx, index); } DirectOperand(index) => { @@ -1733,7 +1755,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { OperandRef::from_immediate_or_packed_pair(bx, llval, ret_abi.layout) }; - self.locals[index] = LocalRef::Operand(op); + self.overwrite_local(index, LocalRef::Operand(op)); self.debug_introduce_local(bx, index); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 14fe84a14..babcf9bee 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -5,7 +5,6 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; -use rustc_span::source_map::Span; use rustc_target::abi::Abi; use super::FunctionCx; @@ -59,22 +58,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } + /// This is a convenience helper for `simd_shuffle_indices`. It has the precondition + /// that the given `constant` is an `ConstantKind::Unevaluated` and must be convertible to + /// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip. + pub fn eval_unevaluated_mir_constant_to_valtree( + &self, + constant: &mir::Constant<'tcx>, + ) -> Result<Option<ty::ValTree<'tcx>>, ErrorHandled> { + let uv = match self.monomorphize(constant.literal) { + mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(), + mir::ConstantKind::Ty(c) => match c.kind() { + // A constant that came from a const generic but was then used as an argument to old-style + // simd_shuffle (passing as argument instead of as a generic param). + rustc_type_ir::ConstKind::Value(valtree) => return Ok(Some(valtree)), + other => span_bug!(constant.span, "{other:#?}"), + }, + // We should never encounter `ConstantKind::Val` unless MIR opts (like const prop) evaluate + // a constant and write that value back into `Operand`s. This could happen, but is unlikely. + // Also: all users of `simd_shuffle` are on unstable and already need to take a lot of care + // around intrinsics. For an issue to happen here, it would require a macro expanding to a + // `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but + // the user pass through arbitrary expressions. + // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real + // const generic. + other => span_bug!(constant.span, "{other:#?}"), + }; + let uv = self.monomorphize(uv); + self.cx.tcx().const_eval_resolve_for_typeck( + ty::ParamEnv::reveal_all(), + uv, + Some(constant.span), + ) + } + /// process constant containing SIMD shuffle indices pub fn simd_shuffle_indices( &mut self, bx: &Bx, - span: Span, - ty: Ty<'tcx>, - constant: Result<ConstValue<'tcx>, ErrorHandled>, + constant: &mir::Constant<'tcx>, ) -> (Bx::Value, Ty<'tcx>) { - constant + let ty = self.monomorphize(constant.ty()); + let val = self + .eval_unevaluated_mir_constant_to_valtree(constant) + .ok() + .flatten() .map(|val| { let field_ty = ty.builtin_index().unwrap(); - let c = mir::ConstantKind::from_value(val, ty); - let values: Vec<_> = bx - .tcx() - .destructure_mir_constant(ty::ParamEnv::reveal_all(), c) - .fields + let values: Vec<_> = val + .unwrap_branch() .iter() .map(|field| { if let Some(prim) = field.try_to_scalar() { @@ -88,15 +119,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }) .collect(); - let llval = bx.const_struct(&values, false); - (llval, c.ty()) + bx.const_struct(&values, false) }) - .unwrap_or_else(|_| { - bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span }); + .unwrap_or_else(|| { + bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span: constant.span }); // We've errored, so we don't have to produce working code. - let ty = self.monomorphize(ty); let llty = bx.backend_type(bx.layout_of(ty)); - (bx.const_undef(llty), ty) - }) + bx.const_undef(llty) + }); + (val, ty) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index f1fe49528..ee7046596 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -1,13 +1,12 @@ use crate::traits::*; -use rustc_middle::mir::coverage::*; use rustc_middle::mir::Coverage; use rustc_middle::mir::SourceScope; use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) { + pub fn codegen_coverage(&self, bx: &mut Bx, coverage: &Coverage, scope: SourceScope) { // Determine the instance that coverage data was originally generated for. let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) { self.monomorphize(inlined) @@ -15,41 +14,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.instance }; - let Coverage { kind, code_region } = coverage; - match kind { - CoverageKind::Counter { function_source_hash, id } => { - if bx.set_function_source_hash(instance, function_source_hash) { - // If `set_function_source_hash()` returned true, the coverage map is enabled, - // so continue adding the counter. - if let Some(code_region) = code_region { - // Note: Some counters do not have code regions, but may still be referenced - // from expressions. In that case, don't add the counter to the coverage map, - // but do inject the counter intrinsic. - bx.add_coverage_counter(instance, id, code_region); - } - - let coverageinfo = bx.tcx().coverageinfo(instance.def); - - let fn_name = bx.get_pgo_func_name_var(instance); - let hash = bx.const_u64(function_source_hash); - let num_counters = bx.const_u32(coverageinfo.num_counters); - let index = bx.const_u32(id.zero_based_index()); - debug!( - "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", - fn_name, hash, num_counters, index, - ); - bx.instrprof_increment(fn_name, hash, num_counters, index); - } - } - CoverageKind::Expression { id, lhs, op, rhs } => { - bx.add_coverage_counter_expression(instance, id, lhs, op, rhs, code_region); - } - CoverageKind::Unreachable => { - bx.add_coverage_unreachable( - instance, - code_region.expect("unreachable regions always have code regions"), - ); - } - } + // Handle the coverage info in a backend-specific way. + bx.add_coverage(instance, coverage); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index bba2800fb..1ee89b3d5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -5,6 +5,7 @@ use rustc_middle::mir; use rustc_middle::ty; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::Ty; use rustc_session::config::DebugInfo; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Span}; @@ -248,7 +249,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } fn spill_operand_to_stack( - operand: &OperandRef<'tcx, Bx::Value>, + operand: OperandRef<'tcx, Bx::Value>, name: Option<String>, bx: &mut Bx, ) -> PlaceRef<'tcx, Bx::Value> { @@ -352,6 +353,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.set_var_name(a, &(name.clone() + ".0")); bx.set_var_name(b, &(name.clone() + ".1")); } + OperandValue::ZeroSized => { + // These never have a value to talk about + } }, LocalRef::PendingOperand => {} } @@ -372,7 +376,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - Self::spill_operand_to_stack(operand, name, bx) + Self::spill_operand_to_stack(*operand, name, bx) } LocalRef::Place(place) => *place, @@ -418,9 +422,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let create_alloca = |bx: &mut Bx, place: PlaceRef<'tcx, Bx::Value>, refcount| { // Create a variable which will be a pointer to the actual value - let ptr_ty = bx - .tcx() - .mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty }); + let ptr_ty = Ty::new_ptr( + bx.tcx(), + ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty }, + ); let ptr_layout = bx.layout_of(ptr_ty); let alloca = PlaceRef::alloca(bx, ptr_layout); bx.set_var_name(alloca.llval, &format!("{}.ref{}.dbg.spill", var.name, refcount)); @@ -522,8 +527,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; for _ in 0..var.references { - var_ty = - bx.tcx().mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty }); + var_ty = Ty::new_ptr( + bx.tcx(), + ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty }, + ); } self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span) @@ -547,7 +554,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) { self.set_debug_loc(bx, var.source_info); let base = Self::spill_operand_to_stack( - &operand, + operand, Some(var.name.to_string()), bx, ); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 1479242f2..8a65dd593 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -211,68 +211,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].val.unaligned_volatile_store(bx, dst); return; } - | sym::unchecked_div - | sym::unchecked_rem - | sym::unchecked_shl - | sym::unchecked_shr - | sym::unchecked_add - | sym::unchecked_sub - | sym::unchecked_mul - | sym::exact_div => { + sym::exact_div => { let ty = arg_tys[0]; match int_type_width_signed(ty, bx.tcx()) { - Some((_width, signed)) => match name { - sym::exact_div => { - if signed { - bx.exactsdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.exactudiv(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_div => { - if signed { - bx.sdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.udiv(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_rem => { - if signed { - bx.srem(args[0].immediate(), args[1].immediate()) - } else { - bx.urem(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()), - sym::unchecked_shr => { - if signed { - bx.ashr(args[0].immediate(), args[1].immediate()) - } else { - bx.lshr(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_add => { - if signed { - bx.unchecked_sadd(args[0].immediate(), args[1].immediate()) - } else { - bx.unchecked_uadd(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_sub => { - if signed { - bx.unchecked_ssub(args[0].immediate(), args[1].immediate()) - } else { - bx.unchecked_usub(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_mul => { - if signed { - bx.unchecked_smul(args[0].immediate(), args[1].immediate()) - } else { - bx.unchecked_umul(args[0].immediate(), args[1].immediate()) - } + Some((_width, signed)) => { + if signed { + bx.exactsdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.exactudiv(args[0].immediate(), args[1].immediate()) } - _ => bug!(), }, None => { bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs new file mode 100644 index 000000000..378c54013 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs @@ -0,0 +1,75 @@ +//! Locals are in a private module as updating `LocalRef::Operand` has to +//! be careful wrt to subtyping. To deal with this we only allow updates by using +//! `FunctionCx::overwrite_local` which handles it automatically. +use crate::mir::{FunctionCx, LocalRef}; +use crate::traits::BuilderMethods; +use rustc_index::IndexVec; +use rustc_middle::mir; +use rustc_middle::ty::print::with_no_trimmed_paths; +use std::ops::{Index, IndexMut}; +pub(super) struct Locals<'tcx, V> { + values: IndexVec<mir::Local, LocalRef<'tcx, V>>, +} + +impl<'tcx, V> Index<mir::Local> for Locals<'tcx, V> { + type Output = LocalRef<'tcx, V>; + #[inline] + fn index(&self, index: mir::Local) -> &LocalRef<'tcx, V> { + &self.values[index] + } +} + +/// To mutate locals, use `FunctionCx::overwrite_local` instead. +impl<'tcx, V, Idx: ?Sized> !IndexMut<Idx> for Locals<'tcx, V> {} + +impl<'tcx, V> Locals<'tcx, V> { + pub(super) fn empty() -> Locals<'tcx, V> { + Locals { values: IndexVec::default() } + } + + pub(super) fn indices(&self) -> impl DoubleEndedIterator<Item = mir::Local> + Clone + 'tcx { + self.values.indices() + } +} + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub(super) fn initialize_locals(&mut self, values: Vec<LocalRef<'tcx, Bx::Value>>) { + assert!(self.locals.values.is_empty()); + // FIXME(#115215): After #115025 get's merged this might not be necessary + for (local, value) in values.into_iter().enumerate() { + match value { + LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (), + LocalRef::Operand(op) => { + let local = mir::Local::from_usize(local); + let expected_ty = self.monomorphize(self.mir.local_decls[local].ty); + if expected_ty != op.layout.ty { + warn!("Unexpected initial operand type. See the issues/114858"); + } + } + } + self.locals.values.push(value); + } + } + + pub(super) fn overwrite_local( + &mut self, + local: mir::Local, + mut value: LocalRef<'tcx, Bx::Value>, + ) { + match value { + LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (), + LocalRef::Operand(ref mut op) => { + let local_ty = self.monomorphize(self.mir.local_decls[local].ty); + if local_ty != op.layout.ty { + // FIXME(#112651): This can be changed to an ICE afterwards. + debug!("updating type of operand due to subtyping"); + with_no_trimmed_paths!(debug!(?op.layout.ty)); + with_no_trimmed_paths!(debug!(?local_ty)); + op.layout.ty = local_ty; + } + } + }; + + self.locals.values[local] = value; + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1204c99e5..9ff6a2497 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -1,21 +1,31 @@ use crate::base; use crate::traits::*; +use rustc_index::bit_set::BitSet; +use rustc_index::IndexVec; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::traversal; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_target::abi::call::{FnAbi, PassMode}; use std::iter; -use rustc_index::bit_set::BitSet; -use rustc_index::IndexVec; +mod analyze; +mod block; +pub mod constant; +pub mod coverageinfo; +pub mod debuginfo; +mod intrinsic; +mod locals; +pub mod operand; +pub mod place; +mod rvalue; +mod statement; use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo}; -use self::place::PlaceRef; -use rustc_middle::mir::traversal; - use self::operand::{OperandRef, OperandValue}; +use self::place::PlaceRef; // Used for tracking the state of generated basic blocks. enum CachedLlbb<T> { @@ -91,7 +101,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// /// Avoiding allocs can also be important for certain intrinsics, /// notably `expect`. - locals: IndexVec<mir::Local, LocalRef<'tcx, Bx::Value>>, + locals: locals::Locals<'tcx, Bx::Value>, /// All `VarDebugInfo` from the MIR body, partitioned by `Local`. /// This is `None` if no var`#[non_exhaustive]`iable debuginfo/names are needed. @@ -111,7 +121,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.instance.subst_mir_and_normalize_erasing_regions( self.cx.tcx(), ty::ParamEnv::reveal_all(), - ty::EarlyBinder(value), + ty::EarlyBinder::bind(value), ) } } @@ -129,16 +139,13 @@ enum LocalRef<'tcx, V> { PendingOperand, } -impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> { - fn new_operand<Bx: BuilderMethods<'a, 'tcx, Value = V>>( - bx: &mut Bx, - layout: TyAndLayout<'tcx>, - ) -> LocalRef<'tcx, V> { +impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> { + fn new_operand(layout: TyAndLayout<'tcx>) -> LocalRef<'tcx, V> { if layout.is_zst() { // Zero-size temporaries aren't always initialized, which // doesn't matter because they don't contain data, but // we need something in the operand. - LocalRef::Operand(OperandRef::new_zst(bx, layout)) + LocalRef::Operand(OperandRef::zero_sized(layout)) } else { LocalRef::PendingOperand } @@ -172,7 +179,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( start_bx.set_personality_fn(cx.eh_personality()); } - let cleanup_kinds = base::wants_msvc_seh(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir)); + let cleanup_kinds = + base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir)); let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> = mir.basic_blocks @@ -195,7 +203,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cleanup_kinds, landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), - locals: IndexVec::new(), + locals: locals::Locals::empty(), debug_context, per_local_var_debug_info: None, caller_location: None, @@ -226,7 +234,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let memory_locals = analyze::non_ssa_locals(&fx); // Allocate variable and temp allocas - fx.locals = { + let local_values = { let args = arg_local_refs(&mut start_bx, &mut fx, &memory_locals); let mut allocate_local = |local| { @@ -249,7 +257,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } else { debug!("alloc: {:?} -> operand", local); - LocalRef::new_operand(&mut start_bx, layout) + LocalRef::new_operand(layout) } }; @@ -259,6 +267,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( .chain(mir.vars_and_temps_iter().map(allocate_local)) .collect() }; + fx.initialize_locals(local_values); // Apply debuginfo to the newly allocated locals. fx.debug_introduce_locals(&mut start_bx); @@ -292,14 +301,13 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( .enumerate() .map(|(arg_index, local)| { let arg_decl = &mir.local_decls[local]; + let arg_ty = fx.monomorphize(arg_decl.ty); if Some(local) == mir.spread_arg { // This argument (e.g., the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have // to reconstruct it into a tuple local variable, from multiple // individual LLVM function arguments. - - let arg_ty = fx.monomorphize(arg_decl.ty); let ty::Tuple(tupled_arg_tys) = arg_ty.kind() else { bug!("spread argument isn't a tuple?!"); }; @@ -334,8 +342,6 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() { - let arg_ty = fx.monomorphize(arg_decl.ty); - let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); bx.va_start(va_list.llval); @@ -355,7 +361,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let local = |op| LocalRef::Operand(op); match arg.mode { PassMode::Ignore => { - return local(OperandRef::new_zst(bx, arg.layout)); + return local(OperandRef::zero_sized(arg.layout)); } PassMode::Direct(_) => { let llarg = bx.get_param(llarg_idx); @@ -432,14 +438,3 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( args } - -mod analyze; -mod block; -pub mod constant; -pub mod coverageinfo; -pub mod debuginfo; -mod intrinsic; -pub mod operand; -pub mod place; -mod rvalue; -mod statement; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 2301c3ef1..31c293d7c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -8,10 +8,10 @@ use crate::traits::*; use crate::MemFlags; use rustc_middle::mir; -use rustc_middle::mir::interpret::{ConstValue, Pointer, Scalar}; +use rustc_middle::mir::interpret::{alloc_range, ConstValue, Pointer, Scalar}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; -use rustc_target::abi::{Abi, Align, Size}; +use rustc_target::abi::{self, Abi, Align, Size}; use std::fmt; @@ -45,6 +45,14 @@ pub enum OperandValue<V> { /// as returned by [`LayoutTypeMethods::scalar_pair_element_backend_type`] /// with `immediate: true`. Pair(V, V), + /// A value taking no bytes, and which therefore needs no LLVM value at all. + /// + /// If you ever need a `V` to pass to something, get a fresh poison value + /// from [`ConstMethods::const_poison`]. + /// + /// An `OperandValue` *must* be this variant for any type for which + /// `is_zst` on its `Layout` returns `true`. + ZeroSized, } /// An `OperandRef` is an "SSA" reference to a Rust value, along with @@ -71,15 +79,9 @@ impl<V: CodegenObject> fmt::Debug for OperandRef<'_, V> { } impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { - pub fn new_zst<Bx: BuilderMethods<'a, 'tcx, Value = V>>( - bx: &mut Bx, - layout: TyAndLayout<'tcx>, - ) -> OperandRef<'tcx, V> { + pub fn zero_sized(layout: TyAndLayout<'tcx>) -> OperandRef<'tcx, V> { assert!(layout.is_zst()); - OperandRef { - val: OperandValue::Immediate(bx.const_poison(bx.immediate_backend_type(layout))), - layout, - } + OperandRef { val: OperandValue::ZeroSized, layout } } pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>( @@ -97,7 +99,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout)); OperandValue::Immediate(llval) } - ConstValue::ZeroSized => return OperandRef::new_zst(bx, layout), + ConstValue::ZeroSized => return OperandRef::zero_sized(layout), ConstValue::Slice { data, start, end } => { let Abi::ScalarPair(a_scalar, _) = layout.abi else { bug!("from_const: invalid ScalarPair layout: {:#?}", layout); @@ -115,13 +117,82 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { OperandValue::Pair(a_llval, b_llval) } ConstValue::ByRef { alloc, offset } => { - return bx.load_operand(bx.from_const_alloc(layout, alloc, offset)); + return Self::from_const_alloc(bx, layout, alloc, offset); } }; OperandRef { val, layout } } + fn from_const_alloc<Bx: BuilderMethods<'a, 'tcx, Value = V>>( + bx: &mut Bx, + layout: TyAndLayout<'tcx>, + alloc: rustc_middle::mir::interpret::ConstAllocation<'tcx>, + offset: Size, + ) -> Self { + let alloc_align = alloc.inner().align; + assert_eq!(alloc_align, layout.align.abi); + let ty = bx.type_ptr_to(bx.cx().backend_type(layout)); + + let read_scalar = |start, size, s: abi::Scalar, ty| { + let val = alloc + .0 + .read_scalar( + bx, + alloc_range(start, size), + /*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)), + ) + .unwrap(); + bx.scalar_to_backend(val, s, ty) + }; + + // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. + // However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned -- + // and yet cannot be represented by an interpreter `Scalar`, since we have to handle the + // case where some of the bytes are initialized and others are not. So, we need an extra + // check that walks over the type of `mplace` to make sure it is truly correct to treat this + // like a `Scalar` (or `ScalarPair`). + match layout.abi { + Abi::Scalar(s @ abi::Scalar::Initialized { .. }) => { + let size = s.size(bx); + assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); + let val = read_scalar(Size::ZERO, size, s, ty); + OperandRef { val: OperandValue::Immediate(val), layout } + } + Abi::ScalarPair( + a @ abi::Scalar::Initialized { .. }, + b @ abi::Scalar::Initialized { .. }, + ) => { + let (a_size, b_size) = (a.size(bx), b.size(bx)); + let b_offset = a_size.align_to(b.align(bx).abi); + assert!(b_offset.bytes() > 0); + let a_val = read_scalar( + Size::ZERO, + a_size, + a, + bx.scalar_pair_element_backend_type(layout, 0, true), + ); + let b_val = read_scalar( + b_offset, + b_size, + b, + bx.scalar_pair_element_backend_type(layout, 1, true), + ); + OperandRef { val: OperandValue::Pair(a_val, b_val), layout } + } + _ if layout.is_zst() => OperandRef::zero_sized(layout), + _ => { + // Neither a scalar nor scalar pair. Load from a place + let init = bx.const_data_from_alloc(alloc); + let base_addr = bx.static_addr_of(init, alloc_align, None); + + let llval = bx.const_ptr_byte_offset(base_addr, offset); + let llval = bx.const_bitcast(llval, ty); + bx.load_operand(PlaceRef::new_sized(llval, layout)) + } + } + } + /// Asserts that this operand refers to a scalar and returns /// a reference to its value. pub fn immediate(self) -> V { @@ -147,6 +218,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { OperandValue::Immediate(llptr) => (llptr, None), OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)), OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self), + OperandValue::ZeroSized => bug!("Deref of ZST operand {:?}", self), }; let layout = cx.layout_of(projected_ty); PlaceRef { llval: llptr, llextra, layout, align: layout.align.abi } @@ -204,9 +276,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let mut val = match (self.val, self.layout.abi) { // If the field is ZST, it has no data. - _ if field.is_zst() => { - return OperandRef::new_zst(bx, field); - } + _ if field.is_zst() => OperandValue::ZeroSized, // Newtype of a scalar, scalar pair or vector. (OperandValue::Immediate(_) | OperandValue::Pair(..), _) @@ -237,6 +307,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { }; match (&mut val, field.abi) { + (OperandValue::ZeroSized, _) => {} ( OperandValue::Immediate(llval), Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. }, @@ -290,8 +361,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { /// Returns an `OperandValue` that's generally UB to use in any way. /// - /// Depending on the `layout`, returns an `Immediate` or `Pair` containing - /// poison value(s), or a `Ref` containing a poison pointer. + /// Depending on the `layout`, returns `ZeroSized` for ZSTs, an `Immediate` or + /// `Pair` containing poison value(s), or a `Ref` containing a poison pointer. /// /// Supports sized types only. pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>( @@ -299,7 +370,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { layout: TyAndLayout<'tcx>, ) -> OperandValue<V> { assert!(layout.is_sized()); - if bx.cx().is_backend_immediate(layout) { + if layout.is_zst() { + OperandValue::ZeroSized + } else if bx.cx().is_backend_immediate(layout) { let ibty = bx.cx().immediate_backend_type(layout); OperandValue::Immediate(bx.const_poison(ibty)) } else if bx.cx().is_backend_scalar_pair(layout) { @@ -352,12 +425,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { flags: MemFlags, ) { debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); - // Avoid generating stores of zero-sized values, because the only way to have a zero-sized - // value is through `undef`, and store itself is useless. - if dest.layout.is_zst() { - return; - } match self { + OperandValue::ZeroSized => { + // Avoid generating stores of zero-sized values, because the only way to have a zero-sized + // value is through `undef`/`poison`, and the store itself is useless. + } OperandValue::Ref(r, None, source_align) => { if flags.contains(MemFlags::NONTEMPORAL) { // HACK(nox): This is inefficient but there is no nontemporal memcpy. @@ -458,7 +530,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // checks in `codegen_consume` and `extract_field`. let elem = o.layout.field(bx.cx(), 0); if elem.is_zst() { - o = OperandRef::new_zst(bx, elem); + o = OperandRef::zero_sized(elem); } else { return None; } @@ -492,7 +564,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // ZSTs don't require any actual memory access. if layout.is_zst() { - return OperandRef::new_zst(bx, layout); + return OperandRef::zero_sized(layout); } if let Some(o) = self.maybe_codegen_consume_direct(bx, place_ref) { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index a58a61cd5..ab493ae5c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { layout: TyAndLayout<'tcx>, ) -> Self { assert!(layout.is_unsized(), "tried to allocate indirect place for sized values"); - let ptr_ty = bx.cx().tcx().mk_mut_ptr(layout.ty); + let ptr_ty = Ty::new_mut_ptr(bx.cx().tcx(), layout.ty); let ptr_layout = bx.cx().layout_of(ptr_ty); Self::alloca(bx, ptr_layout) } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 6e7065713..956f03d25 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -11,7 +11,7 @@ use rustc_middle::mir; use rustc_middle::mir::Operand; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_target::abi::{self, FIRST_VARIANT}; @@ -32,7 +32,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cg_operand.val.store(bx, dest); } - mir::Rvalue::Cast(mir::CastKind::Pointer(PointerCast::Unsize), ref source, _) => { + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::Unsize), + ref source, + _, + ) => { // The destination necessarily contains a fat pointer, so if // it's a scalar pair, it's a fat pointer or newtype thereof. if bx.cx().is_backend_scalar_pair(dest.layout) { @@ -70,6 +74,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Ref(_, Some(_), _) => { bug!("unsized coercion on an unsized rvalue"); } + OperandValue::ZeroSized => { + bug!("unsized coercion on a ZST rvalue"); + } } } @@ -165,11 +172,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } match src.val { - OperandValue::Ref(..) => { + OperandValue::Ref(..) | OperandValue::ZeroSized => { span_bug!( self.mir.span, "Operand path should have handled transmute \ - from `Ref` {src:?} to place {dst:?}" + from {src:?} to place {dst:?}" ); } OperandValue::Immediate(..) | OperandValue::Pair(..) => { @@ -220,17 +227,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let fake_place = PlaceRef::new_sized_aligned(cast_ptr, cast, align); Some(bx.load_operand(fake_place).val) } + OperandValue::ZeroSized => { + let OperandValueKind::ZeroSized = operand_kind else { + bug!("Found {operand_kind:?} for operand {operand:?}"); + }; + if let OperandValueKind::ZeroSized = cast_kind { + Some(OperandValue::ZeroSized) + } else { + None + } + } OperandValue::Immediate(imm) => { let OperandValueKind::Immediate(in_scalar) = operand_kind else { bug!("Found {operand_kind:?} for operand {operand:?}"); }; - if let OperandValueKind::Immediate(out_scalar) = cast_kind { - match (in_scalar, out_scalar) { - (ScalarOrZst::Zst, ScalarOrZst::Zst) => { - Some(OperandRef::new_zst(bx, cast).val) - } - (ScalarOrZst::Scalar(in_scalar), ScalarOrZst::Scalar(out_scalar)) - if in_scalar.size(self.cx) == out_scalar.size(self.cx) => + if let OperandValueKind::Immediate(out_scalar) = cast_kind + && in_scalar.size(self.cx) == out_scalar.size(self.cx) { let operand_bty = bx.backend_type(operand.layout); let cast_bty = bx.backend_type(cast); @@ -242,9 +254,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { out_scalar, cast_bty, ))) - } - _ => None, - } } else { None } @@ -406,7 +415,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let lladdr = bx.ptrtoint(llptr, llcast_ty); OperandValue::Immediate(lladdr) } - mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => { + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { match *operand.layout.ty.kind() { ty::FnDef(def_id, substs) => { let instance = ty::Instance::resolve_for_fn_ptr( @@ -422,7 +431,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } } - mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)) => { + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)) => { match *operand.layout.ty.kind() { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( @@ -438,11 +447,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), } } - mir::CastKind::Pointer(PointerCast::UnsafeFnPointer) => { + mir::CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { // This is a no-op at the LLVM level. operand.val } - mir::CastKind::Pointer(PointerCast::Unsize) => { + mir::CastKind::PointerCoercion(PointerCoercion::Unsize) => { assert!(bx.cx().is_backend_scalar_pair(cast)); let (lldata, llextra) = match operand.val { OperandValue::Pair(lldata, llextra) => { @@ -457,12 +466,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Ref(..) => { bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand); } + OperandValue::ZeroSized => { + bug!("zero-sized operand {:?} in `codegen_rvalue_operand`", operand); + } }; let (lldata, llextra) = base::unsize_ptr(bx, lldata, operand.layout.ty, cast.ty, llextra); OperandValue::Pair(lldata, llextra) } - mir::CastKind::Pointer(PointerCast::MutToConstPointer) + mir::CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) | mir::CastKind::PtrToPtr if bx.cx().is_backend_scalar_pair(operand.layout) => { @@ -490,13 +502,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Ref(_, _, _) => todo!(), OperandValue::Immediate(v) => (v, None), OperandValue::Pair(v, l) => (v, Some(l)), + OperandValue::ZeroSized => bug!("ZST -- which is not PointerLike -- in DynStar"), }; let (lldata, llextra) = base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra); OperandValue::Pair(lldata, llextra) } - mir::CastKind::Pointer( - PointerCast::MutToConstPointer | PointerCast::ArrayToPointer, + mir::CastKind::PointerCoercion( + PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, ) | mir::CastKind::IntToInt | mir::CastKind::FloatToInt @@ -572,7 +585,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Ref(_, bk, place) => { let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { - tcx.mk_ref( + Ty::new_ref( + tcx, tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() }, ) @@ -583,7 +597,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::CopyForDeref(place) => self.codegen_operand(bx, &Operand::Copy(place)), mir::Rvalue::AddressOf(mutability, place) => { let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { - tcx.mk_ptr(ty::TypeAndMut { ty, mutbl: mutability }) + Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl: mutability }) }; self.codegen_place_to_pointer(bx, place, mk_ptr) } @@ -635,7 +649,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { lhs.layout.ty, ); let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); - let operand_ty = bx.tcx().mk_tup(&[val_ty, bx.tcx().types.bool]); + let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]); OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) } } @@ -668,11 +682,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::NullaryOp(ref null_op, ty) => { let ty = self.monomorphize(ty); - assert!(bx.cx().type_is_sized(ty)); let layout = bx.cx().layout_of(ty); let val = match null_op { - mir::NullOp::SizeOf => layout.size.bytes(), - mir::NullOp::AlignOf => layout.align.abi.bytes(), + mir::NullOp::SizeOf => { + assert!(bx.cx().type_is_sized(ty)); + layout.size.bytes() + } + mir::NullOp::AlignOf => { + assert!(bx.cx().type_is_sized(ty)); + layout.align.abi.bytes() + } mir::NullOp::OffsetOf(fields) => { layout.offset_of_subfield(bx.cx(), fields.iter().map(|f| f.index())).bytes() } @@ -713,14 +732,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // According to `rvalue_creates_operand`, only ZST // aggregate rvalues are allowed to be operands. let ty = rvalue.ty(self.mir, self.cx.tcx()); - OperandRef::new_zst(bx, self.cx.layout_of(self.monomorphize(ty))) + OperandRef::zero_sized(self.cx.layout_of(self.monomorphize(ty))) } mir::Rvalue::ShallowInitBox(ref operand, content_ty) => { let operand = self.codegen_operand(bx, operand); let lloperand = operand.immediate(); let content_ty = self.monomorphize(content_ty); - let box_layout = bx.cx().layout_of(bx.tcx().mk_box(content_ty)); + let box_layout = bx.cx().layout_of(Ty::new_box(bx.tcx(), content_ty)); let llty_ptr = bx.cx().backend_type(box_layout); let val = bx.pointercast(lloperand, llty_ptr); @@ -784,6 +803,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.add(lhs, rhs) } } + mir::BinOp::AddUnchecked => { + if is_signed { + bx.unchecked_sadd(lhs, rhs) + } else { + bx.unchecked_uadd(lhs, rhs) + } + } mir::BinOp::Sub => { if is_float { bx.fsub(lhs, rhs) @@ -791,6 +817,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.sub(lhs, rhs) } } + mir::BinOp::SubUnchecked => { + if is_signed { + bx.unchecked_ssub(lhs, rhs) + } else { + bx.unchecked_usub(lhs, rhs) + } + } mir::BinOp::Mul => { if is_float { bx.fmul(lhs, rhs) @@ -798,6 +831,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.mul(lhs, rhs) } } + mir::BinOp::MulUnchecked => { + if is_signed { + bx.unchecked_smul(lhs, rhs) + } else { + bx.unchecked_umul(lhs, rhs) + } + } mir::BinOp::Div => { if is_float { bx.fdiv(lhs, rhs) @@ -834,8 +874,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inbounds_gep(llty, lhs, &[rhs]) } } - mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs), - mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs), + mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs), + mir::BinOp::ShlUnchecked => { + let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + bx.shl(lhs, rhs) + } + mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs), + mir::BinOp::ShrUnchecked => { + let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } + } mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt @@ -931,6 +979,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Can always load from a pointer as needed (OperandValueKind::Ref, _) => true, + // ZST-to-ZST is the easiest thing ever + (OperandValueKind::ZeroSized, OperandValueKind::ZeroSized) => true, + + // But if only one of them is a ZST the sizes can't match + (OperandValueKind::ZeroSized, _) | (_, OperandValueKind::ZeroSized) => false, + // Need to generate an `alloc` to get a pointer from an immediate (OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false, @@ -974,12 +1028,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Gets which variant of [`OperandValue`] is expected for a particular type. fn value_kind(&self, layout: TyAndLayout<'tcx>) -> OperandValueKind { - if self.cx.is_backend_immediate(layout) { + if layout.is_zst() { + OperandValueKind::ZeroSized + } else if self.cx.is_backend_immediate(layout) { debug_assert!(!self.cx.is_backend_scalar_pair(layout)); OperandValueKind::Immediate(match layout.abi { - abi::Abi::Scalar(s) => ScalarOrZst::Scalar(s), - abi::Abi::Vector { element, .. } => ScalarOrZst::Scalar(element), - _ if layout.is_zst() => ScalarOrZst::Zst, + abi::Abi::Scalar(s) => s, + abi::Abi::Vector { element, .. } => element, x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"), }) } else if self.cx.is_backend_scalar_pair(layout) { @@ -1002,21 +1057,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[derive(Debug, Copy, Clone)] enum OperandValueKind { Ref, - Immediate(ScalarOrZst), + Immediate(abi::Scalar), Pair(abi::Scalar, abi::Scalar), -} - -#[derive(Debug, Copy, Clone)] -enum ScalarOrZst { - Zst, - Scalar(abi::Scalar), -} - -impl ScalarOrZst { - pub fn size(self, cx: &impl abi::HasDataLayout) -> abi::Size { - match self { - ScalarOrZst::Zst => abi::Size::ZERO, - ScalarOrZst::Scalar(s) => s.size(cx), - } - } + ZeroSized, } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 3fd7397ad..899e41265 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -20,7 +20,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } LocalRef::PendingOperand => { let operand = self.codegen_rvalue_operand(bx, rvalue); - self.locals[index] = LocalRef::Operand(operand); + self.overwrite_local(index, LocalRef::Operand(operand)); self.debug_introduce_local(bx, index); } LocalRef::Operand(op) => { @@ -65,7 +65,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } mir::StatementKind::Coverage(box ref coverage) => { - self.codegen_coverage(bx, coverage.clone(), statement.source_info.scope); + self.codegen_coverage(bx, coverage, statement.source_info.scope); } mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => { let op_val = self.codegen_operand(bx, op); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index c5976a654..9e06fec55 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -44,6 +44,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ // #[target_feature]. ("thumb-mode", Some(sym::arm_target_feature)), ("thumb2", Some(sym::arm_target_feature)), + ("trustzone", Some(sym::arm_target_feature)), ("v5te", Some(sym::arm_target_feature)), ("v6", Some(sym::arm_target_feature)), ("v6k", Some(sym::arm_target_feature)), @@ -53,6 +54,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ ("vfp2", Some(sym::arm_target_feature)), ("vfp3", Some(sym::arm_target_feature)), ("vfp4", Some(sym::arm_target_feature)), + ("virtualization", Some(sym::arm_target_feature)), // tidy-alphabetical-end ]; @@ -282,6 +284,7 @@ const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ // tidy-alphabetical-start ("atomics", Some(sym::wasm_target_feature)), ("bulk-memory", Some(sym::wasm_target_feature)), + ("exception-handling", Some(sym::wasm_target_feature)), ("multivalue", Some(sym::wasm_target_feature)), ("mutable-globals", Some(sym::wasm_target_feature)), ("nontrapping-fptoint", Some(sym::wasm_target_feature)), diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index d83bfc740..b3c9ecf8b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -6,7 +6,7 @@ use crate::back::write::TargetMachineFactoryFn; use crate::{CodegenResults, ModuleCodegen}; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; @@ -101,7 +101,7 @@ pub trait CodegenBackend { ongoing_codegen: Box<dyn Any>, sess: &Session, outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed>; + ) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed>; /// This is called on the returned `Box<dyn Any>` from `join_codegen` /// diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 619063027..d6e9bfce1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -1,8 +1,6 @@ use super::BackendTypes; -use crate::mir::place::PlaceRef; use rustc_middle::mir::interpret::{ConstAllocation, Scalar}; -use rustc_middle::ty::layout::TyAndLayout; -use rustc_target::abi::{self, Size}; +use rustc_target::abi; pub trait ConstMethods<'tcx>: BackendTypes { // Constant constructors @@ -17,6 +15,7 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_i32(&self, i: i32) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; fn const_u64(&self, i: u64) -> Self::Value; + fn const_u128(&self, i: u128) -> Self::Value; fn const_usize(&self, i: u64) -> Self::Value; fn const_u8(&self, i: u8) -> Self::Value; fn const_real(&self, t: Self::Type, val: f64) -> Self::Value; @@ -30,12 +29,8 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value; fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; - fn from_const_alloc( - &self, - layout: TyAndLayout<'tcx>, - alloc: ConstAllocation<'tcx>, - offset: Size, - ) -> PlaceRef<'tcx, Self::Value>; fn const_ptrcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value; + fn const_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value; + fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index e77201cf0..7e8de0ddc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -1,57 +1,11 @@ use super::BackendTypes; -use rustc_hir::def_id::DefId; -use rustc_middle::mir::coverage::*; +use rustc_middle::mir::Coverage; use rustc_middle::ty::Instance; -pub trait CoverageInfoMethods<'tcx>: BackendTypes { - fn coverageinfo_finalize(&self); - - /// Codegen a small function that will never be called, with one counter - /// that will never be incremented, that gives LLVM coverage tools a - /// function definition it needs in order to resolve coverage map references - /// to unused functions. This is necessary so unused functions will appear - /// as uncovered (coverage execution count `0`) in LLVM coverage reports. - fn define_unused_fn(&self, def_id: DefId); - - /// For LLVM codegen, returns a function-specific `Value` for a global - /// string, to hold the function name passed to LLVM intrinsic - /// `instrprof.increment()`. The `Value` is only created once per instance. - /// Multiple invocations with the same instance return the same `Value`. - fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; -} - pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { - /// Returns true if the function source hash was added to the coverage map (even if it had - /// already been added, for this instance). Returns false *only* if `-C instrument-coverage` is - /// not enabled (a coverage map is not being generated). - fn set_function_source_hash( - &mut self, - instance: Instance<'tcx>, - function_source_hash: u64, - ) -> bool; - - /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage` - /// is not enabled (a coverage map is not being generated). - fn add_coverage_counter( - &mut self, - instance: Instance<'tcx>, - index: CounterValueReference, - region: CodeRegion, - ) -> bool; - - /// Returns true if the expression was added to the coverage map; false if - /// `-C instrument-coverage` is not enabled (a coverage map is not being generated). - fn add_coverage_counter_expression( - &mut self, - instance: Instance<'tcx>, - id: InjectedExpressionId, - lhs: ExpressionOperandId, - op: Op, - rhs: ExpressionOperandId, - region: Option<CodeRegion>, - ) -> bool; - - /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage` - /// is not enabled (a coverage map is not being generated). - fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool; + /// Handle the MIR coverage info in a backend-specific way. + /// + /// This can potentially be a no-op in backends that don't support + /// coverage instrumentation. + fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage); } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 782fdadbf..8cb58bd4c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -33,7 +33,7 @@ pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAs pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; -pub use self::coverageinfo::{CoverageInfoBuilderMethods, CoverageInfoMethods}; +pub use self::coverageinfo::CoverageInfoBuilderMethods; pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods}; pub use self::declare::PreDefineMethods; pub use self::intrinsic::IntrinsicCallMethods; @@ -59,7 +59,6 @@ pub trait CodegenMethods<'tcx>: + MiscMethods<'tcx> + ConstMethods<'tcx> + StaticMethods - + CoverageInfoMethods<'tcx> + DebugInfoMethods<'tcx> + AsmMethods<'tcx> + PreDefineMethods<'tcx> @@ -75,7 +74,6 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where + MiscMethods<'tcx> + ConstMethods<'tcx> + StaticMethods - + CoverageInfoMethods<'tcx> + DebugInfoMethods<'tcx> + AsmMethods<'tcx> + PreDefineMethods<'tcx> diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 36d986422..e64417e1a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -126,6 +126,28 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { index: usize, immediate: bool, ) -> Self::Type; + + /// A type that can be used in a [`super::BuilderMethods::load`] + + /// [`super::BuilderMethods::store`] pair to implement a *typed* copy, + /// such as a MIR `*_0 = *_1`. + /// + /// It's always legal to return `None` here, as the provided impl does, + /// in which case callers should use [`super::BuilderMethods::memcpy`] + /// instead of the `load`+`store` pair. + /// + /// This can be helpful for things like arrays, where the LLVM backend type + /// `[3 x i16]` optimizes to three separate loads and stores, but it can + /// instead be copied via an `i48` that stays as the single `load`+`store`. + /// (As of 2023-05 LLVM cannot necessarily optimize away a `memcpy` in these + /// cases, due to `poison` handling, but in codegen we have more information + /// about the type invariants, so can emit something better instead.) + /// + /// This *should* return `None` for particularly-large types, where leaving + /// the `memcpy` may well be important to avoid code size explosion. + fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option<Self::Type> { + let _ = layout; + None + } } // For backends that support CFI using type membership (i.e., testing whether a given pointer is |