From ecbba4731f2bdbe90479ffad6d50f1c0f51cd6d9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 25 Aug 2025 07:22:38 +1000 Subject: [PATCH] Move metadata generation. It's currently done within `start_codegen`, which is called from `codegen_and_build_linker`, and it gets included in the codegen timing section. All this is surprising, because it's not part of codegen. This commit moves it into `run_compiler`, just before the call to `codegen_and_build_linker`. This is a more sensible place for it. The commit also adds some extra non-obvious information about the `has_errors_or_delayed_bugs` check, which I learned when I tried moving metadata generation earlier. Likewise, the nearby `rustc_delayed_bug_from_inside_query` code is also moved, because (a) it's not part of codegen, and (b) it needs to be nearby. --- compiler/rustc_driver_impl/src/lib.rs | 28 ++++++++++++++++++-- compiler/rustc_interface/src/passes.rs | 21 ++------------- compiler/rustc_interface/src/queries.rs | 3 ++- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 ++ tests/ui-fulldeps/run-compiler-twice.rs | 4 ++- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index f3ed604210565..d5507e1ca5ccd 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -60,8 +60,8 @@ use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; use rustc_session::output::{CRATE_TYPES, collect_crate_types, invalid_output_for_target}; use rustc_session::{EarlyDiagCtxt, Session, config}; -use rustc_span::FileName; use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::{FileName, sym}; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTuple}; use tracing::trace; @@ -383,7 +383,31 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } } - Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)) + // Hook for tests. This must be shortly before the + // `has_errors_or_delayed_bugs` check below for + // `tests/ui/treat-err-as-bug/span_delayed_bug.rs` to work. + if let Some((def_id, _)) = tcx.entry_fn(()) + && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query) + { + tcx.ensure_ok().trigger_delayed_bug(def_id); + } + + // Don't emit metadata or do codegen if there were any errors. + // + // Note: checking for delayed bugs here is aggressive. If we have + // no errors but one or more delayed bugs, the `raise_fatal` call + // will instantly ICE when the DiagCtxt is dropped. I.e. this check + // serves as a kind of barrier, giving no opportunity for a + // subsequent error (e.g. during codegen) to nullify a delayed bug. + // The rationale is that delayed bugs are likely to cause more ICEs + // during codegen, obscuring the original problem. + if let Some(guar) = tcx.sess.dcx().has_errors_or_delayed_bugs() { + guar.raise_fatal(); + } + + let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx); + + Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend, metadata)) }); // Linking is done outside the `compiler.enter()` so that the diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index bc5ef04079ed4..77eb0f20f2550 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -21,7 +21,6 @@ use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; -use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepsType; @@ -1196,33 +1195,17 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, -) -> (Box, EncodedMetadata) { +) -> Box { tcx.sess.timings.start_section(tcx.sess.dcx(), TimingSection::Codegen); - // Hook for tests. - if let Some((def_id, _)) = tcx.entry_fn(()) - && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query) - { - tcx.ensure_ok().trigger_delayed_bug(def_id); - } - // Don't run this test assertions when not doing codegen. Compiletest tries to build // build-fail tests in check mode first and expects it to not give an error in that case. if tcx.sess.opts.output_types.should_codegen() { rustc_symbol_mangling::test::report_symbol_names(tcx); } - // Don't do code generation if there were any errors. Likewise if - // there were any delayed bugs, because codegen will likely cause - // more ICEs, obscuring the original problem. - if let Some(guar) = tcx.sess.dcx().has_errors_or_delayed_bugs() { - guar.raise_fatal(); - } - info!("Pre-codegen\n{:?}", tcx.debug_stats()); - let metadata = rustc_metadata::fs::encode_and_write_metadata(tcx); - let codegen = tcx.sess.time("codegen_crate", move || codegen_backend.codegen_crate(tcx)); info!("Post-codegen\n{:?}", tcx.debug_stats()); @@ -1233,7 +1216,7 @@ pub(crate) fn start_codegen<'tcx>( tcx.sess.code_stats.print_type_sizes(); } - (codegen, metadata) + codegen } /// Compute and validate the crate name. diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 370e886c525fd..2fa8e76097024 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -28,8 +28,9 @@ impl Linker { pub fn codegen_and_build_linker( tcx: TyCtxt<'_>, codegen_backend: &dyn CodegenBackend, + metadata: EncodedMetadata, ) -> Linker { - let (ongoing_codegen, metadata) = passes::start_codegen(codegen_backend, tcx); + let ongoing_codegen = passes::start_codegen(codegen_backend, tcx); Linker { dep_graph: tcx.dep_graph.clone(), diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index a7e7e9985f4d4..5023d628e29e9 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2274,6 +2274,8 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { // will allow us to slice the metadata to the precise length that we just // generated regardless of trailing bytes that end up in it. +// The `Default` impl is for tests. +#[derive(Default)] pub struct EncodedMetadata { // The declaration order matters because `full_metadata` should be dropped // before `_temp_dir`. diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index 87504b8301f03..584da2c6751b4 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -12,6 +12,7 @@ extern crate rustc_driver; extern crate rustc_interface; +extern crate rustc_metadata; extern crate rustc_session; extern crate rustc_span; @@ -81,7 +82,8 @@ fn compile(code: String, output: PathBuf, sysroot: Sysroot, linker: Option<&Path let krate = rustc_interface::passes::parse(&compiler.sess); let linker = rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| { let _ = tcx.analysis(()); - Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend) + let metadata = Default::default(); + Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend, metadata) }); linker.link(&compiler.sess, &*compiler.codegen_backend); });