about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-02-10 11:38:45 +0000
committerbors <bors@rust-lang.org>2025-02-10 11:38:45 +0000
commit8c04e395952022a451138dc4dbead6dd6ae65203 (patch)
treead0062d7c194bb6841e3fa09c09fe6d28c08bdc0
parent80c091958f05e573433df974f8d2f2bc3a3eadfb (diff)
parentffa8a96040b8bb1ae8b856ae2a95159ffe1cb400 (diff)
downloadrust-8c04e395952022a451138dc4dbead6dd6ae65203.tar.gz
rust-8c04e395952022a451138dc4dbead6dd6ae65203.zip
Auto merge of #136809 - workingjubilee:rollup-jk0pew1, r=workingjubilee
Rollup of 12 pull requests

Successful merges:

 - #136053 (coverage: Defer part of counter-creation until codegen)
 - #136201 (Removed dependency on the field-offset crate, alternate approach)
 - #136228 (Simplify Rc::as_ptr docs + typo fix)
 - #136353 (fix(libtest): Enable Instant on Emscripten targets)
 - #136472 ([`compiletest`-related cleanups 2/7] Feed stage number to compiletest directly)
 - #136487 (ci: stop mysql before removing it)
 - #136552 (Use an `Option` for `FindNextFileHandle` in `ReadDir` instead of `INVALID_FILE_HANDLE` sentinel value)
 - #136705 (Some miscellaneous edition-related library tweaks)
 - #136707 (Bump `cc` to v1.2.13 for the compiler workspace)
 - #136790 (Git blame ignore recent formatting commit)
 - #136792 (Don't apply editorconfig to llvm)
 - #136805 (ignore win_delete_self test in Miri)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--.editorconfig2
-rw-r--r--.git-blame-ignore-revs2
-rw-r--r--Cargo.lock25
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs55
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs23
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml2
-rw-r--r--compiler/rustc_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/mir/coverage.rs139
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs6
-rw-r--r--compiler/rustc_middle/src/query/mod.rs13
-rw-r--r--compiler/rustc_middle/src/query/plumbing.rs7
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs103
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs25
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs10
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs61
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs112
-rw-r--r--compiler/rustc_mir_transform/src/coverage/query.rs167
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs3
-rw-r--r--compiler/rustc_query_impl/Cargo.toml1
-rw-r--r--compiler/rustc_query_impl/src/lib.rs17
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs4
-rw-r--r--library/alloc/src/alloc.rs6
-rw-r--r--library/alloc/src/collections/btree/merge_iter.rs2
-rw-r--r--library/alloc/src/ffi/c_str.rs2
-rw-r--r--library/alloc/src/rc.rs6
-rw-r--r--library/core/src/ffi/c_str.rs2
-rw-r--r--library/core/src/ffi/mod.rs2
-rw-r--r--library/core/src/intrinsics/mod.rs2
-rw-r--r--library/core/src/iter/sources/once_with.rs14
-rw-r--r--library/core/src/panicking.rs4
-rw-r--r--library/core/src/ptr/metadata.rs2
-rw-r--r--library/coretests/tests/mem.rs2
-rw-r--r--library/coretests/tests/num/flt2dec/mod.rs2
-rw-r--r--library/coretests/tests/ptr.rs6
-rw-r--r--library/panic_abort/src/lib.rs4
-rw-r--r--library/panic_abort/src/zkvm.rs2
-rw-r--r--library/panic_unwind/src/emcc.rs4
-rw-r--r--library/panic_unwind/src/hermit.rs4
-rw-r--r--library/panic_unwind/src/lib.rs2
-rw-r--r--library/panic_unwind/src/miri.rs2
-rw-r--r--library/panic_unwind/src/seh.rs6
-rw-r--r--library/rtstartup/rsbegin.rs8
-rw-r--r--library/rtstartup/rsend.rs2
-rw-r--r--library/std/src/alloc.rs2
-rw-r--r--library/std/src/panicking.rs4
-rw-r--r--library/std/src/sync/mpmc/mod.rs6
-rw-r--r--library/std/src/sys/alloc/xous.rs2
-rw-r--r--library/std/src/sys/cmath.rs2
-rw-r--r--library/std/src/sys/pal/hermit/mod.rs2
-rw-r--r--library/std/src/sys/pal/itron/abi.rs2
-rw-r--r--library/std/src/sys/pal/sgx/abi/mem.rs2
-rw-r--r--library/std/src/sys/pal/sgx/abi/mod.rs2
-rw-r--r--library/std/src/sys/pal/sgx/abi/panic.rs2
-rw-r--r--library/std/src/sys/pal/sgx/abi/reloc.rs2
-rw-r--r--library/std/src/sys/pal/sgx/abi/thread.rs2
-rw-r--r--library/std/src/sys/pal/sgx/abi/tls/mod.rs2
-rw-r--r--library/std/src/sys/pal/sgx/abi/usercalls/raw.rs2
-rw-r--r--library/std/src/sys/pal/solid/abi/fs.rs2
-rw-r--r--library/std/src/sys/pal/solid/abi/mod.rs10
-rw-r--r--library/std/src/sys/pal/solid/abi/sockets.rs2
-rw-r--r--library/std/src/sys/pal/solid/os.rs2
-rw-r--r--library/std/src/sys/pal/teeos/thread.rs2
-rw-r--r--library/std/src/sys/pal/unix/args.rs4
-rw-r--r--library/std/src/sys/pal/unix/futex.rs4
-rw-r--r--library/std/src/sys/pal/unix/mod.rs20
-rw-r--r--library/std/src/sys/pal/unix/os.rs14
-rw-r--r--library/std/src/sys/pal/unix/process/zircon.rs4
-rw-r--r--library/std/src/sys/pal/unix/thread.rs6
-rw-r--r--library/std/src/sys/pal/unix/thread_parking.rs2
-rw-r--r--library/std/src/sys/pal/unix/weak.rs2
-rw-r--r--library/std/src/sys/pal/wasi/fs.rs2
-rw-r--r--library/std/src/sys/pal/wasi/os.rs4
-rw-r--r--library/std/src/sys/pal/wasi/thread.rs2
-rw-r--r--library/std/src/sys/pal/windows/c.rs4
-rw-r--r--library/std/src/sys/pal/windows/compat.rs2
-rw-r--r--library/std/src/sys/pal/windows/fs.rs16
-rw-r--r--library/std/src/sys/pal/windows/process/tests.rs2
-rw-r--r--library/std/src/sys/pal/xous/os.rs2
-rw-r--r--library/std/src/sys/pal/zkvm/abi.rs2
-rw-r--r--library/std/src/sys/personality/gcc.rs2
-rw-r--r--library/std/src/sys/random/arc4random.rs2
-rw-r--r--library/std/src/sys/random/espidf.rs2
-rw-r--r--library/std/src/sys/random/fuchsia.rs2
-rw-r--r--library/std/src/sys/random/teeos.rs2
-rw-r--r--library/std/src/sys/sync/thread_parking/darwin.rs2
-rw-r--r--library/std/src/sys/thread_local/destructors/linux_like.rs2
-rw-r--r--library/std/src/sys/thread_local/guard/apple.rs2
-rw-r--r--library/std/src/sys/thread_local/guard/windows.rs2
-rw-r--r--library/std/src/sys/thread_local/key/unix.rs2
-rw-r--r--library/std/src/sys/thread_local/key/xous.rs2
-rw-r--r--library/std/tests/switch-stdout.rs4
-rw-r--r--library/std/tests/win_delete_self.rs1
-rw-r--r--library/test/src/console.rs5
-rw-r--r--library/test/src/lib.rs8
-rw-r--r--library/test/src/term/win.rs2
-rw-r--r--library/unwind/src/lib.rs46
-rw-r--r--library/unwind/src/libunwind.rs16
-rw-r--r--library/windows_targets/src/lib.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs21
-rwxr-xr-xsrc/ci/scripts/free-disk-space.sh3
-rw-r--r--src/tools/compiletest/src/common.rs4
-rw-r--r--src/tools/compiletest/src/header/cfg.rs2
-rw-r--r--src/tools/compiletest/src/header/tests.rs10
-rw-r--r--src/tools/compiletest/src/lib.rs10
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs32
-rw-r--r--src/tools/tidy/src/deps.rs4
-rw-r--r--tests/coverage/assert_not.cov-map8
-rw-r--r--tests/coverage/bad_counter_ids.cov-map24
-rw-r--r--tests/coverage/fn_sig_into_try.cov-map24
-rw-r--r--tests/coverage/inline-dead.cov-map9
-rw-r--r--tests/coverage/issue-84561.cov-map29
-rw-r--r--tests/coverage/loops_branches.cov-map95
-rw-r--r--tests/coverage/no_spans_if_not.cov-map8
-rw-r--r--tests/coverage/tight_inf_loop.cov-map8
-rw-r--r--tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff26
-rw-r--r--tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff4
-rw-r--r--tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff18
-rw-r--r--tests/mir-opt/coverage/instrument_coverage.rs8
-rw-r--r--tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff18
-rw-r--r--tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff18
-rw-r--r--tests/mir-opt/coverage/instrument_coverage_cleanup.rs4
123 files changed, 596 insertions, 908 deletions
diff --git a/.editorconfig b/.editorconfig
index eadd72e4993..ef8ed24c52a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,6 +9,8 @@ end_of_line = lf
 charset = utf-8
 trim_trailing_whitespace = true
 insert_final_newline = true
+
+[!src/llvm-project]
 indent_style = space
 indent_size = 4
 
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 8612a40923a..d2a017bd2a0 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -29,3 +29,5 @@ ec2cc761bc7067712ecc7734502f703fe3b024c8
 99cb0c6bc399fb94a0ddde7e9b38e9c00d523bad
 # reformat with rustfmt edition 2024
 c682aa162b0d41e21cc6748f4fecfe01efb69d1f
+# reformat with updated edition 2024
+1fcae03369abb4c2cc180cd5a49e1f4440a81300
diff --git a/Cargo.lock b/Cargo.lock
index fc4c7c9888f..2ee2c52b32a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -420,9 +420,9 @@ version = "0.1.0"
 
 [[package]]
 name = "cc"
-version = "1.2.7"
+version = "1.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
+checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda"
 dependencies = [
  "shlex",
 ]
@@ -1210,16 +1210,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "field-offset"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
-dependencies = [
- "memoffset",
- "rustc_version",
-]
-
-[[package]]
 name = "filetime"
 version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2296,15 +2286,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "memoffset"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
 name = "mime"
 version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4173,7 +4154,6 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "either",
- "field-offset",
  "gsgdt",
  "polonius-engine",
  "rustc-rayon-core",
@@ -4421,7 +4401,6 @@ dependencies = [
 name = "rustc_query_impl"
 version = "0.0.0"
 dependencies = [
- "field-offset",
  "measureme",
  "rustc_data_structures",
  "rustc_errors",
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
index 460a4664615..c53ea6d4666 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
@@ -11,7 +11,8 @@ use rustc_codegen_ssa::traits::{
     BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
 };
 use rustc_middle::mir::coverage::{
-    CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
+    BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping,
+    MappingKind, Op,
 };
 use rustc_middle::ty::{Instance, TyCtxt};
 use rustc_span::Span;
@@ -53,7 +54,7 @@ pub(crate) fn prepare_covfun_record<'tcx>(
     let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?;
     let ids_info = tcx.coverage_ids_info(instance.def)?;
 
-    let expressions = prepare_expressions(fn_cov_info, ids_info, is_used);
+    let expressions = prepare_expressions(ids_info);
 
     let mut covfun = CovfunRecord {
         mangled_function_name: tcx.symbol_name(instance).name,
@@ -75,26 +76,14 @@ pub(crate) fn prepare_covfun_record<'tcx>(
 }
 
 /// Convert the function's coverage-counter expressions into a form suitable for FFI.
-fn prepare_expressions(
-    fn_cov_info: &FunctionCoverageInfo,
-    ids_info: &CoverageIdsInfo,
-    is_used: bool,
-) -> Vec<ffi::CounterExpression> {
-    // If any counters or expressions were removed by MIR opts, replace their
-    // terms with zero.
-    let counter_for_term = |term| {
-        if !is_used || ids_info.is_zero_term(term) {
-            ffi::Counter::ZERO
-        } else {
-            ffi::Counter::from_term(term)
-        }
-    };
+fn prepare_expressions(ids_info: &CoverageIdsInfo) -> Vec<ffi::CounterExpression> {
+    let counter_for_term = ffi::Counter::from_term;
 
     // We know that LLVM will optimize out any unused expressions before
     // producing the final coverage map, so there's no need to do the same
     // thing on the Rust side unless we're confident we can do much better.
     // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
-    fn_cov_info
+    ids_info
         .expressions
         .iter()
         .map(move |&Expression { lhs, op, rhs }| ffi::CounterExpression {
@@ -136,11 +125,16 @@ fn fill_region_tables<'tcx>(
 
     // For each counter/region pair in this function+file, convert it to a
     // form suitable for FFI.
-    let is_zero_term = |term| !covfun.is_used || ids_info.is_zero_term(term);
     for &Mapping { ref kind, span } in &fn_cov_info.mappings {
-        // If the mapping refers to counters/expressions that were removed by
-        // MIR opts, replace those occurrences with zero.
-        let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term });
+        // If this function is unused, replace all counters with zero.
+        let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
+            let term = if covfun.is_used {
+                ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term")
+            } else {
+                CovTerm::Zero
+            };
+            ffi::Counter::from_term(term)
+        };
 
         // Convert the `Span` into coordinates that we can pass to LLVM, or
         // discard the span if conversion fails. In rare, cases _all_ of a
@@ -154,23 +148,22 @@ fn fill_region_tables<'tcx>(
             continue;
         }
 
-        match kind {
-            MappingKind::Code(term) => {
-                code_regions
-                    .push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) });
+        match *kind {
+            MappingKind::Code { bcb } => {
+                code_regions.push(ffi::CodeRegion { cov_span, counter: counter_for_bcb(bcb) });
             }
-            MappingKind::Branch { true_term, false_term } => {
+            MappingKind::Branch { true_bcb, false_bcb } => {
                 branch_regions.push(ffi::BranchRegion {
                     cov_span,
-                    true_counter: ffi::Counter::from_term(true_term),
-                    false_counter: ffi::Counter::from_term(false_term),
+                    true_counter: counter_for_bcb(true_bcb),
+                    false_counter: counter_for_bcb(false_bcb),
                 });
             }
-            MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
+            MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params } => {
                 mcdc_branch_regions.push(ffi::MCDCBranchRegion {
                     cov_span,
-                    true_counter: ffi::Counter::from_term(true_term),
-                    false_counter: ffi::Counter::from_term(false_term),
+                    true_counter: counter_for_bcb(true_bcb),
+                    false_counter: counter_for_bcb(false_bcb),
                     mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
                 });
             }
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 021108cd51c..ea7f581a3cb 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -160,21 +160,12 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
             CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
                 "marker statement {kind:?} should have been removed by CleanupPostBorrowck"
             ),
-            CoverageKind::CounterIncrement { id } => {
-                // The number of counters passed to `llvm.instrprof.increment` might
-                // be smaller than the number originally inserted by the instrumentor,
-                // if some high-numbered counters were removed by MIR optimizations.
-                // If so, LLVM's profiler runtime will use fewer physical counters.
-                let num_counters = ids_info.num_counters_after_mir_opts();
-                assert!(
-                    num_counters as usize <= function_coverage_info.num_counters,
-                    "num_counters disagreement: query says {num_counters} but function info only has {}",
-                    function_coverage_info.num_counters
-                );
-
+            CoverageKind::VirtualCounter { bcb }
+                if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) =>
+            {
                 let fn_name = bx.get_pgo_func_name_var(instance);
                 let hash = bx.const_u64(function_coverage_info.function_source_hash);
-                let num_counters = bx.const_u32(num_counters);
+                let num_counters = bx.const_u32(ids_info.num_counters);
                 let index = bx.const_u32(id.as_u32());
                 debug!(
                     "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
@@ -182,10 +173,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
                 );
                 bx.instrprof_increment(fn_name, hash, num_counters, index);
             }
-            CoverageKind::ExpressionUsed { id: _ } => {
-                // Expression-used statements are markers that are handled by
-                // `coverage_ids_info`, so there's nothing to codegen here.
-            }
+            // If a BCB doesn't have an associated physical counter, there's nothing to codegen.
+            CoverageKind::VirtualCounter { .. } => {}
             CoverageKind::CondBitmapUpdate { index, decision_depth } => {
                 let cond_bitmap = coverage_cx
                     .try_get_mcdc_condition_bitmap(&instance, decision_depth)
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 14346795fda..f0d04b2b644 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -13,6 +13,7 @@
 #![feature(extern_types)]
 #![feature(file_buffered)]
 #![feature(hash_raw_entry)]
+#![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index b22357acdc2..2afc1efc1b3 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -11,7 +11,7 @@ bitflags = "2.4.1"
 bstr = "1.11.3"
 # Pinned so `cargo update` bumps don't cause breakage. Please also update the
 # `cc` in `rustc_llvm` if you update the `cc` here.
-cc = "=1.2.7"
+cc = "=1.2.13"
 either = "1.5.0"
 itertools = "0.12"
 pathdiff = "0.2.0"
diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml
index 7c402310436..2168a0df9ec 100644
--- a/compiler/rustc_llvm/Cargo.toml
+++ b/compiler/rustc_llvm/Cargo.toml
@@ -12,5 +12,5 @@ libc = "0.2.73"
 # tidy-alphabetical-start
 # Pinned so `cargo update` bumps don't cause breakage. Please also update the
 # pinned `cc` in `rustc_codegen_ssa` if you update `cc` here.
-cc = "=1.2.7"
+cc = "=1.2.13"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 2c34df6ea61..de722e62043 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
 # tidy-alphabetical-start
 bitflags = "2.4.1"
 either = "1.5.0"
-field-offset = "0.3.5"
 gsgdt = "0.1.2"
 polonius-engine = "0.13.0"
 rustc-rayon-core = { version = "0.5.0" }
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index 46534697e1d..8c6b11a681e 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -2,8 +2,8 @@
 
 use std::fmt::{self, Debug, Formatter};
 
-use rustc_index::IndexVec;
-use rustc_index::bit_set::DenseBitSet;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_index::{Idx, IndexVec};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_span::Span;
 
@@ -103,23 +103,12 @@ pub enum CoverageKind {
     /// Should be erased before codegen (at some point after `InstrumentCoverage`).
     BlockMarker { id: BlockMarkerId },
 
-    /// Marks the point in MIR control flow represented by a coverage counter.
+    /// Marks its enclosing basic block with the ID of the coverage graph node
+    /// that it was part of during the `InstrumentCoverage` MIR pass.
     ///
-    /// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
-    ///
-    /// If this statement does not survive MIR optimizations, any mappings that
-    /// refer to this counter can have those references simplified to zero.
-    CounterIncrement { id: CounterId },
-
-    /// Marks the point in MIR control-flow represented by a coverage expression.
-    ///
-    /// If this statement does not survive MIR optimizations, any mappings that
-    /// refer to this expression can have those references simplified to zero.
-    ///
-    /// (This is only inserted for expression IDs that are directly used by
-    /// mappings. Intermediate expressions with no direct mappings are
-    /// retained/zeroed based on whether they are transitively used.)
-    ExpressionUsed { id: ExpressionId },
+    /// During codegen, this might be lowered to `llvm.instrprof.increment` or
+    /// to a no-op, depending on the outcome of counter-creation.
+    VirtualCounter { bcb: BasicCoverageBlock },
 
     /// Marks the point in MIR control flow represented by a evaluated condition.
     ///
@@ -138,8 +127,7 @@ impl Debug for CoverageKind {
         match self {
             SpanMarker => write!(fmt, "SpanMarker"),
             BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
-            CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
-            ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
+            VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"),
             CondBitmapUpdate { index, decision_depth } => {
                 write!(fmt, "CondBitmapUpdate(index={:?}, depth={:?})", index, decision_depth)
             }
@@ -179,34 +167,19 @@ pub struct Expression {
 #[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub enum MappingKind {
     /// Associates a normal region of code with a counter/expression/zero.
-    Code(CovTerm),
+    Code { bcb: BasicCoverageBlock },
     /// Associates a branch region with separate counters for true and false.
-    Branch { true_term: CovTerm, false_term: CovTerm },
+    Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
     /// Associates a branch region with separate counters for true and false.
-    MCDCBranch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo },
+    MCDCBranch {
+        true_bcb: BasicCoverageBlock,
+        false_bcb: BasicCoverageBlock,
+        mcdc_params: ConditionInfo,
+    },
     /// Associates a decision region with a bitmap and number of conditions.
     MCDCDecision(DecisionInfo),
 }
 
-impl MappingKind {
-    /// Returns a copy of this mapping kind, in which all coverage terms have
-    /// been replaced with ones returned by the given function.
-    pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self {
-        match *self {
-            Self::Code(term) => Self::Code(map_fn(term)),
-            Self::Branch { true_term, false_term } => {
-                Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) }
-            }
-            Self::MCDCBranch { true_term, false_term, mcdc_params } => Self::MCDCBranch {
-                true_term: map_fn(true_term),
-                false_term: map_fn(false_term),
-                mcdc_params,
-            },
-            Self::MCDCDecision(param) => Self::MCDCDecision(param),
-        }
-    }
-}
-
 #[derive(Clone, Debug)]
 #[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct Mapping {
@@ -222,10 +195,15 @@ pub struct Mapping {
 pub struct FunctionCoverageInfo {
     pub function_source_hash: u64,
     pub body_span: Span,
-    pub num_counters: usize,
-    pub mcdc_bitmap_bits: usize,
-    pub expressions: IndexVec<ExpressionId, Expression>,
+
+    /// Used in conjunction with `priority_list` to create physical counters
+    /// and counter expressions, after MIR optimizations.
+    pub node_flow_data: NodeFlowData<BasicCoverageBlock>,
+    pub priority_list: Vec<BasicCoverageBlock>,
+
     pub mappings: Vec<Mapping>,
+
+    pub mcdc_bitmap_bits: usize,
     /// The depth of the deepest decision is used to know how many
     /// temp condbitmaps should be allocated for the function.
     pub mcdc_num_condition_bitmaps: usize,
@@ -292,40 +270,55 @@ pub struct MCDCDecisionSpan {
     pub num_conditions: usize,
 }
 
-/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
-/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
-/// have had a chance to potentially remove some of them.
+/// Contains information needed during codegen, obtained by inspecting the
+/// function's MIR after MIR optimizations.
 ///
-/// Used by the `coverage_ids_info` query.
+/// Returned by the `coverage_ids_info` query.
 #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
 pub struct CoverageIdsInfo {
-    pub counters_seen: DenseBitSet<CounterId>,
-    pub zero_expressions: DenseBitSet<ExpressionId>,
+    pub num_counters: u32,
+    pub phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
+    pub term_for_bcb: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
+    pub expressions: IndexVec<ExpressionId, Expression>,
 }
 
-impl CoverageIdsInfo {
-    /// Coverage codegen needs to know how many coverage counters are ever
-    /// incremented within a function, so that it can set the `num-counters`
-    /// argument of the `llvm.instrprof.increment` intrinsic.
+rustc_index::newtype_index! {
+    /// During the `InstrumentCoverage` MIR pass, a BCB is a node in the
+    /// "coverage graph", which is a refinement of the MIR control-flow graph
+    /// that merges or omits some blocks that aren't relevant to coverage.
     ///
-    /// This may be less than the highest counter ID emitted by the
-    /// InstrumentCoverage MIR pass, if the highest-numbered counter increments
-    /// were removed by MIR optimizations.
-    pub fn num_counters_after_mir_opts(&self) -> u32 {
-        // FIXME(Zalathar): Currently this treats an unused counter as "used"
-        // if its ID is less than that of the highest counter that really is
-        // used. Fixing this would require adding a renumbering step somewhere.
-        self.counters_seen.last_set_in(..).map_or(0, |max| max.as_u32() + 1)
+    /// After that pass is complete, the coverage graph no longer exists, so a
+    /// BCB is effectively an opaque ID.
+    #[derive(HashStable)]
+    #[encodable]
+    #[orderable]
+    #[debug_format = "bcb{}"]
+    pub struct BasicCoverageBlock {
+        const START_BCB = 0;
     }
+}
 
-    /// Returns `true` if the given term is known to have a value of zero, taking
-    /// into account knowledge of which counters are unused and which expressions
-    /// are always zero.
-    pub fn is_zero_term(&self, term: CovTerm) -> bool {
-        match term {
-            CovTerm::Zero => true,
-            CovTerm::Counter(id) => !self.counters_seen.contains(id),
-            CovTerm::Expression(id) => self.zero_expressions.contains(id),
-        }
-    }
+/// Data representing a view of some underlying graph, in which each node's
+/// successors have been merged into a single "supernode".
+///
+/// The resulting supernodes have no obvious meaning on their own.
+/// However, merging successor nodes means that a node's out-edges can all
+/// be combined into a single out-edge, whose flow is the same as the flow
+/// (execution count) of its corresponding node in the original graph.
+///
+/// With all node flows now in the original graph now represented as edge flows
+/// in the merged graph, it becomes possible to analyze the original node flows
+/// using techniques for analyzing edge flows.
+#[derive(Clone, Debug)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
+pub struct NodeFlowData<Node: Idx> {
+    /// Maps each node to the supernode that contains it, indicated by some
+    /// arbitrary "root" node that is part of that supernode.
+    pub supernodes: IndexVec<Node, Node>,
+    /// For each node, stores the single supernode that all of its successors
+    /// have been merged into.
+    ///
+    /// (Note that each node in a supernode can potentially have a _different_
+    /// successor supernode from its peers.)
+    pub succ_supernodes: IndexVec<Node, Node>,
 }
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 3007b787496..11ebbbe807d 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -619,13 +619,9 @@ fn write_function_coverage_info(
     function_coverage_info: &coverage::FunctionCoverageInfo,
     w: &mut dyn io::Write,
 ) -> io::Result<()> {
-    let coverage::FunctionCoverageInfo { body_span, expressions, mappings, .. } =
-        function_coverage_info;
+    let coverage::FunctionCoverageInfo { body_span, mappings, .. } = function_coverage_info;
 
     writeln!(w, "{INDENT}coverage body span: {body_span:?}")?;
-    for (id, expression) in expressions.iter_enumerated() {
-        writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
-    }
     for coverage::Mapping { kind, span } in mappings {
         writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?;
     }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d94efe2d7d6..478ac19d199 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -614,9 +614,16 @@ rustc_queries! {
         feedable
     }
 
-    /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
-    /// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
-    /// have had a chance to potentially remove some of them.
+    /// Scans through a function's MIR after MIR optimizations, to prepare the
+    /// information needed by codegen when `-Cinstrument-coverage` is active.
+    ///
+    /// This includes the details of where to insert `llvm.instrprof.increment`
+    /// intrinsics, and the expression tables to be embedded in the function's
+    /// coverage metadata.
+    ///
+    /// FIXME(Zalathar): This query's purpose has drifted a bit and should
+    /// probably be renamed, but that can wait until after the potential
+    /// follow-ups to #136053 have settled down.
     ///
     /// Returns `None` for functions that were not instrumented.
     query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> {
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index 6c019b427db..690b8128b1a 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -1,6 +1,5 @@
 use std::ops::Deref;
 
-use field_offset::FieldOffset;
 use rustc_data_structures::sync::{AtomicU64, WorkerLocal};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::hir_id::OwnerId;
@@ -24,8 +23,10 @@ pub struct DynamicQuery<'tcx, C: QueryCache> {
     pub eval_always: bool,
     pub dep_kind: DepKind,
     pub handle_cycle_error: HandleCycleError,
-    pub query_state: FieldOffset<QueryStates<'tcx>, QueryState<C::Key>>,
-    pub query_cache: FieldOffset<QueryCaches<'tcx>, C>,
+    // Offset of this query's state field in the QueryStates struct
+    pub query_state: usize,
+    // Offset of this query's cache field in the QueryCaches struct
+    pub query_cache: usize,
     pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool,
     pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value,
     pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value,
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index adb99a75a9e..6f9984d5d0a 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -2,7 +2,6 @@ use std::cmp::Ordering;
 
 use either::Either;
 use itertools::Itertools;
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::graph::DirectedGraph;
 use rustc_index::IndexVec;
@@ -11,31 +10,35 @@ use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId,
 
 use crate::coverage::counters::balanced_flow::BalancedFlowGraph;
 use crate::coverage::counters::node_flow::{
-    CounterTerm, NodeCounters, make_node_counters, node_flow_data_for_balanced_graph,
+    CounterTerm, NodeCounters, NodeFlowData, node_flow_data_for_balanced_graph,
 };
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
 
 mod balanced_flow;
-mod node_flow;
+pub(crate) mod node_flow;
 mod union_find;
 
-/// Ensures that each BCB node needing a counter has one, by creating physical
-/// counters or counter expressions for nodes as required.
-pub(super) fn make_bcb_counters(
-    graph: &CoverageGraph,
-    bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
-) -> CoverageCounters {
+/// Struct containing the results of [`prepare_bcb_counters_data`].
+pub(crate) struct BcbCountersData {
+    pub(crate) node_flow_data: NodeFlowData<BasicCoverageBlock>,
+    pub(crate) priority_list: Vec<BasicCoverageBlock>,
+}
+
+/// Analyzes the coverage graph to create intermediate data structures that
+/// will later be used (during codegen) to create physical counters or counter
+/// expressions for each BCB node that needs one.
+pub(crate) fn prepare_bcb_counters_data(graph: &CoverageGraph) -> BcbCountersData {
     // Create the derived graphs that are necessary for subsequent steps.
     let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable);
     let node_flow_data = node_flow_data_for_balanced_graph(&balanced_graph);
 
-    // Use those graphs to determine which nodes get physical counters, and how
-    // to compute the execution counts of other nodes from those counters.
+    // Also create a "priority list" of coverage graph nodes, to help determine
+    // which ones get physical counters or counter expressions. This needs to
+    // be done now, because the later parts of the counter-creation process
+    // won't have access to the original coverage graph.
     let priority_list = make_node_flow_priority_list(graph, balanced_graph);
-    let node_counters = make_node_counters(&node_flow_data, &priority_list);
 
-    // Convert the counters into a form suitable for embedding into MIR.
-    transcribe_counters(&node_counters, bcb_needs_counter)
+    BcbCountersData { node_flow_data, priority_list }
 }
 
 /// Arranges the nodes in `balanced_graph` into a list, such that earlier nodes
@@ -74,31 +77,33 @@ fn make_node_flow_priority_list(
 }
 
 // Converts node counters into a form suitable for embedding into MIR.
-fn transcribe_counters(
+pub(crate) fn transcribe_counters(
     old: &NodeCounters<BasicCoverageBlock>,
     bcb_needs_counter: &DenseBitSet<BasicCoverageBlock>,
+    bcbs_seen: &DenseBitSet<BasicCoverageBlock>,
 ) -> CoverageCounters {
     let mut new = CoverageCounters::with_num_bcbs(bcb_needs_counter.domain_size());
 
     for bcb in bcb_needs_counter.iter() {
+        if !bcbs_seen.contains(bcb) {
+            // This BCB's code was removed by MIR opts, so its counter is always zero.
+            new.set_node_counter(bcb, CovTerm::Zero);
+            continue;
+        }
+
         // Our counter-creation algorithm doesn't guarantee that a node's list
         // of terms starts or ends with a positive term, so partition the
         // counters into "positive" and "negative" lists for easier handling.
-        let (mut pos, mut neg): (Vec<_>, Vec<_>) =
-            old.counter_terms[bcb].iter().partition_map(|&CounterTerm { node, op }| match op {
+        let (mut pos, mut neg): (Vec<_>, Vec<_>) = old.counter_terms[bcb]
+            .iter()
+            // Filter out any BCBs that were removed by MIR opts;
+            // this treats them as having an execution count of 0.
+            .filter(|term| bcbs_seen.contains(term.node))
+            .partition_map(|&CounterTerm { node, op }| match op {
                 Op::Add => Either::Left(node),
                 Op::Subtract => Either::Right(node),
             });
 
-        if pos.is_empty() {
-            // If we somehow end up with no positive terms, fall back to
-            // creating a physical counter. There's no known way for this
-            // to happen, but we can avoid an ICE if it does.
-            debug_assert!(false, "{bcb:?} has no positive counter terms");
-            pos = vec![bcb];
-            neg = vec![];
-        }
-
         // These intermediate sorts are not strictly necessary, but were helpful
         // in reducing churn when switching to the current counter-creation scheme.
         // They also help to slightly decrease the overall size of the expression
@@ -116,7 +121,7 @@ fn transcribe_counters(
         pos.sort();
         neg.sort();
 
-        let pos_counter = new.make_sum(&pos).expect("`pos` should not be empty");
+        let pos_counter = new.make_sum(&pos).unwrap_or(CovTerm::Zero);
         let new_counter = new.make_subtracted_sum(pos_counter, &neg);
         new.set_node_counter(bcb, new_counter);
     }
@@ -129,15 +134,15 @@ fn transcribe_counters(
 pub(super) struct CoverageCounters {
     /// List of places where a counter-increment statement should be injected
     /// into MIR, each with its corresponding counter ID.
-    phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
-    next_counter_id: CounterId,
+    pub(crate) phys_counter_for_node: FxIndexMap<BasicCoverageBlock, CounterId>,
+    pub(crate) next_counter_id: CounterId,
 
     /// Coverage counters/expressions that are associated with individual BCBs.
-    node_counters: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
+    pub(crate) node_counters: IndexVec<BasicCoverageBlock, Option<CovTerm>>,
 
     /// Table of expression data, associating each expression ID with its
     /// corresponding operator (+ or -) and its LHS/RHS operands.
-    expressions: IndexVec<ExpressionId, Expression>,
+    pub(crate) expressions: IndexVec<ExpressionId, Expression>,
     /// Remember expressions that have already been created (or simplified),
     /// so that we don't create unnecessary duplicates.
     expressions_memo: FxHashMap<Expression, CovTerm>,
@@ -188,12 +193,6 @@ impl CoverageCounters {
         self.make_expression(lhs, Op::Subtract, rhs_sum)
     }
 
-    pub(super) fn num_counters(&self) -> usize {
-        let num_counters = self.phys_counter_for_node.len();
-        assert_eq!(num_counters, self.next_counter_id.as_usize());
-        num_counters
-    }
-
     fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: CovTerm) -> CovTerm {
         let existing = self.node_counters[bcb].replace(counter);
         assert!(
@@ -202,34 +201,4 @@ impl CoverageCounters {
         );
         counter
     }
-
-    pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> {
-        self.node_counters[bcb]
-    }
-
-    /// Returns an iterator over all the nodes in the coverage graph that
-    /// should have a counter-increment statement injected into MIR, along with
-    /// each site's corresponding counter ID.
-    pub(super) fn counter_increment_sites(
-        &self,
-    ) -> impl Iterator<Item = (CounterId, BasicCoverageBlock)> + Captures<'_> {
-        self.phys_counter_for_node.iter().map(|(&site, &id)| (id, site))
-    }
-
-    /// Returns an iterator over the subset of BCB nodes that have been associated
-    /// with a counter *expression*, along with the ID of that expression.
-    pub(super) fn bcb_nodes_with_coverage_expressions(
-        &self,
-    ) -> impl Iterator<Item = (BasicCoverageBlock, ExpressionId)> + Captures<'_> {
-        self.node_counters.iter_enumerated().filter_map(|(bcb, &counter)| match counter {
-            // Yield the BCB along with its associated expression ID.
-            Some(CovTerm::Expression(id)) => Some((bcb, id)),
-            // This BCB is associated with a counter or nothing, so skip it.
-            Some(CovTerm::Counter { .. } | CovTerm::Zero) | None => None,
-        })
-    }
-
-    pub(super) fn into_expressions(self) -> IndexVec<ExpressionId, Expression> {
-        self.expressions
-    }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
index 9d80b3af42d..91ed54b8b59 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
@@ -9,6 +9,7 @@
 use rustc_data_structures::graph;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
+pub(crate) use rustc_middle::mir::coverage::NodeFlowData;
 use rustc_middle::mir::coverage::Op;
 
 use crate::coverage::counters::union_find::UnionFind;
@@ -16,30 +17,6 @@ use crate::coverage::counters::union_find::UnionFind;
 #[cfg(test)]
 mod tests;
 
-/// Data representing a view of some underlying graph, in which each node's
-/// successors have been merged into a single "supernode".
-///
-/// The resulting supernodes have no obvious meaning on their own.
-/// However, merging successor nodes means that a node's out-edges can all
-/// be combined into a single out-edge, whose flow is the same as the flow
-/// (execution count) of its corresponding node in the original graph.
-///
-/// With all node flows now in the original graph now represented as edge flows
-/// in the merged graph, it becomes possible to analyze the original node flows
-/// using techniques for analyzing edge flows.
-#[derive(Debug)]
-pub(crate) struct NodeFlowData<Node: Idx> {
-    /// Maps each node to the supernode that contains it, indicated by some
-    /// arbitrary "root" node that is part of that supernode.
-    supernodes: IndexVec<Node, Node>,
-    /// For each node, stores the single supernode that all of its successors
-    /// have been merged into.
-    ///
-    /// (Note that each node in a supernode can potentially have a _different_
-    /// successor supernode from its peers.)
-    succ_supernodes: IndexVec<Node, Node>,
-}
-
 /// Creates a "merged" view of an underlying graph.
 ///
 /// The given graph is assumed to have [“balanced flow”](balanced-flow),
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 392b54c8d81..6d84248ddfb 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -8,6 +8,7 @@ use rustc_data_structures::graph::dominators::Dominators;
 use rustc_data_structures::graph::{self, DirectedGraph, StartNode};
 use rustc_index::IndexVec;
 use rustc_index::bit_set::DenseBitSet;
+pub(crate) use rustc_middle::mir::coverage::{BasicCoverageBlock, START_BCB};
 use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind};
 use tracing::debug;
 
@@ -269,15 +270,6 @@ impl graph::Predecessors for CoverageGraph {
     }
 }
 
-rustc_index::newtype_index! {
-    /// A node in the control-flow graph of CoverageGraph.
-    #[orderable]
-    #[debug_format = "bcb{}"]
-    pub(crate) struct BasicCoverageBlock {
-        const START_BCB = 0;
-    }
-}
-
 /// `BasicCoverageBlockData` holds the data indexed by a `BasicCoverageBlock`.
 ///
 /// A `BasicCoverageBlock` (BCB) represents the maximal-length sequence of MIR `BasicBlock`s without
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index 8d0d92dc367..d83c0d40a7e 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -1,9 +1,7 @@
 use std::collections::BTreeSet;
 
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_data_structures::graph::DirectedGraph;
 use rustc_index::IndexVec;
-use rustc_index::bit_set::DenseBitSet;
 use rustc_middle::mir::coverage::{
     BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageInfoHi, CoverageKind,
 };
@@ -63,10 +61,6 @@ const MCDC_MAX_BITMAP_SIZE: usize = i32::MAX as usize;
 
 #[derive(Default)]
 pub(super) struct ExtractedMappings {
-    /// Store our own copy of [`CoverageGraph::num_nodes`], so that we don't
-    /// need access to the whole graph when allocating per-BCB data. This is
-    /// only public so that other code can still use exhaustive destructuring.
-    pub(super) num_bcbs: usize,
     pub(super) code_mappings: Vec<CodeMapping>,
     pub(super) branch_pairs: Vec<BranchPair>,
     pub(super) mcdc_bitmap_bits: usize,
@@ -118,7 +112,6 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
     );
 
     ExtractedMappings {
-        num_bcbs: graph.num_nodes(),
         code_mappings,
         branch_pairs,
         mcdc_bitmap_bits,
@@ -127,60 +120,6 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
     }
 }
 
-impl ExtractedMappings {
-    pub(super) fn all_bcbs_with_counter_mappings(&self) -> DenseBitSet<BasicCoverageBlock> {
-        // Fully destructure self to make sure we don't miss any fields that have mappings.
-        let Self {
-            num_bcbs,
-            code_mappings,
-            branch_pairs,
-            mcdc_bitmap_bits: _,
-            mcdc_degraded_branches,
-            mcdc_mappings,
-        } = self;
-
-        // Identify which BCBs have one or more mappings.
-        let mut bcbs_with_counter_mappings = DenseBitSet::new_empty(*num_bcbs);
-        let mut insert = |bcb| {
-            bcbs_with_counter_mappings.insert(bcb);
-        };
-
-        for &CodeMapping { span: _, bcb } in code_mappings {
-            insert(bcb);
-        }
-        for &BranchPair { true_bcb, false_bcb, .. } in branch_pairs {
-            insert(true_bcb);
-            insert(false_bcb);
-        }
-        for &MCDCBranch { true_bcb, false_bcb, .. } in mcdc_degraded_branches
-            .iter()
-            .chain(mcdc_mappings.iter().map(|(_, branches)| branches.into_iter()).flatten())
-        {
-            insert(true_bcb);
-            insert(false_bcb);
-        }
-
-        // MC/DC decisions refer to BCBs, but don't require those BCBs to have counters.
-        if bcbs_with_counter_mappings.is_empty() {
-            debug_assert!(
-                mcdc_mappings.is_empty(),
-                "A function with no counter mappings shouldn't have any decisions: {mcdc_mappings:?}",
-            );
-        }
-
-        bcbs_with_counter_mappings
-    }
-
-    /// Returns the set of BCBs that have one or more `Code` mappings.
-    pub(super) fn bcbs_with_ordinary_code_mappings(&self) -> DenseBitSet<BasicCoverageBlock> {
-        let mut bcbs = DenseBitSet::new_empty(self.num_bcbs);
-        for &CodeMapping { span: _, bcb } in &self.code_mappings {
-            bcbs.insert(bcb);
-        }
-        bcbs
-    }
-}
-
 fn resolve_block_markers(
     coverage_info_hi: &CoverageInfoHi,
     mir_body: &mir::Body<'_>,
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 15487d05a30..e195681bc92 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -21,7 +21,7 @@ use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 use tracing::{debug, debug_span, trace};
 
-use crate::coverage::counters::CoverageCounters;
+use crate::coverage::counters::BcbCountersData;
 use crate::coverage::graph::CoverageGraph;
 use crate::coverage::mappings::ExtractedMappings;
 
@@ -82,28 +82,21 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
     let extracted_mappings =
         mappings::extract_all_mapping_info_from_mir(tcx, mir_body, &hir_info, &graph);
 
-    ////////////////////////////////////////////////////
-    // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
-    // every coverage span has a `Counter` or `Expression` assigned to its `BasicCoverageBlock`
-    // and all `Expression` dependencies (operands) are also generated, for any other
-    // `BasicCoverageBlock`s not already associated with a coverage span.
-    let bcbs_with_counter_mappings = extracted_mappings.all_bcbs_with_counter_mappings();
-    if bcbs_with_counter_mappings.is_empty() {
-        // No relevant spans were found in MIR, so skip instrumenting this function.
-        return;
-    }
-
-    let coverage_counters = counters::make_bcb_counters(&graph, &bcbs_with_counter_mappings);
-
-    let mappings = create_mappings(&extracted_mappings, &coverage_counters);
+    let mappings = create_mappings(&extracted_mappings);
     if mappings.is_empty() {
         // No spans could be converted into valid mappings, so skip this function.
         debug!("no spans could be converted into valid mappings; skipping");
         return;
     }
 
-    inject_coverage_statements(mir_body, &graph, &extracted_mappings, &coverage_counters);
+    // Use the coverage graph to prepare intermediate data that will eventually
+    // be used to assign physical counters and counter expressions to points in
+    // the control-flow graph
+    let BcbCountersData { node_flow_data, priority_list } =
+        counters::prepare_bcb_counters_data(&graph);
 
+    // Inject coverage statements into MIR.
+    inject_coverage_statements(mir_body, &graph);
     inject_mcdc_statements(mir_body, &graph, &extracted_mappings);
 
     let mcdc_num_condition_bitmaps = extracted_mappings
@@ -116,29 +109,25 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
     mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
         function_source_hash: hir_info.function_source_hash,
         body_span: hir_info.body_span,
-        num_counters: coverage_counters.num_counters(),
-        mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
-        expressions: coverage_counters.into_expressions(),
+
+        node_flow_data,
+        priority_list,
+
         mappings,
+
+        mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits,
         mcdc_num_condition_bitmaps,
     }));
 }
 
-/// For each coverage span extracted from MIR, create a corresponding
-/// mapping.
+/// For each coverage span extracted from MIR, create a corresponding mapping.
 ///
-/// Precondition: All BCBs corresponding to those spans have been given
-/// coverage counters.
-fn create_mappings(
-    extracted_mappings: &ExtractedMappings,
-    coverage_counters: &CoverageCounters,
-) -> Vec<Mapping> {
-    let term_for_bcb =
-        |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters");
-
+/// FIXME(Zalathar): This used to be where BCBs in the extracted mappings were
+/// resolved to a `CovTerm`. But that is now handled elsewhere, so this
+/// function can potentially be simplified even further.
+fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec<Mapping> {
     // Fully destructure the mappings struct to make sure we don't miss any kinds.
     let ExtractedMappings {
-        num_bcbs: _,
         code_mappings,
         branch_pairs,
         mcdc_bitmap_bits: _,
@@ -150,23 +139,18 @@ fn create_mappings(
     mappings.extend(code_mappings.iter().map(
         // Ordinary code mappings are the simplest kind.
         |&mappings::CodeMapping { span, bcb }| {
-            let kind = MappingKind::Code(term_for_bcb(bcb));
+            let kind = MappingKind::Code { bcb };
             Mapping { kind, span }
         },
     ));
 
     mappings.extend(branch_pairs.iter().map(
         |&mappings::BranchPair { span, true_bcb, false_bcb }| {
-            let true_term = term_for_bcb(true_bcb);
-            let false_term = term_for_bcb(false_bcb);
-            let kind = MappingKind::Branch { true_term, false_term };
+            let kind = MappingKind::Branch { true_bcb, false_bcb };
             Mapping { kind, span }
         },
     ));
 
-    let term_for_bcb =
-        |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters");
-
     // MCDC branch mappings are appended with their decisions in case decisions were ignored.
     mappings.extend(mcdc_degraded_branches.iter().map(
         |&mappings::MCDCBranch {
@@ -176,11 +160,7 @@ fn create_mappings(
              condition_info: _,
              true_index: _,
              false_index: _,
-         }| {
-            let true_term = term_for_bcb(true_bcb);
-            let false_term = term_for_bcb(false_bcb);
-            Mapping { kind: MappingKind::Branch { true_term, false_term }, span }
-        },
+         }| { Mapping { kind: MappingKind::Branch { true_bcb, false_bcb }, span } },
     ));
 
     for (decision, branches) in mcdc_mappings {
@@ -201,12 +181,10 @@ fn create_mappings(
                      true_index: _,
                      false_index: _,
                  }| {
-                    let true_term = term_for_bcb(true_bcb);
-                    let false_term = term_for_bcb(false_bcb);
                     Mapping {
                         kind: MappingKind::MCDCBranch {
-                            true_term,
-                            false_term,
+                            true_bcb,
+                            false_bcb,
                             mcdc_params: condition_info,
                         },
                         span,
@@ -227,41 +205,11 @@ fn create_mappings(
     mappings
 }
 
-/// For each BCB node or BCB edge that has an associated coverage counter,
-/// inject any necessary coverage statements into MIR.
-fn inject_coverage_statements<'tcx>(
-    mir_body: &mut mir::Body<'tcx>,
-    graph: &CoverageGraph,
-    extracted_mappings: &ExtractedMappings,
-    coverage_counters: &CoverageCounters,
-) {
-    // Inject counter-increment statements into MIR.
-    for (id, bcb) in coverage_counters.counter_increment_sites() {
-        let target_bb = graph[bcb].leader_bb();
-        inject_statement(mir_body, CoverageKind::CounterIncrement { id }, target_bb);
-    }
-
-    // For each counter expression that is directly associated with at least one
-    // span, we inject an "expression-used" statement, so that coverage codegen
-    // can check whether the injected statement survived MIR optimization.
-    // (BCB edges can't have spans, so we only need to process BCB nodes here.)
-    //
-    // We only do this for ordinary `Code` mappings, because branch and MC/DC
-    // mappings might have expressions that don't correspond to any single
-    // point in the control-flow graph.
-    //
-    // See the code in `rustc_codegen_llvm::coverageinfo::map_data` that deals
-    // with "expressions seen" and "zero terms".
-    let eligible_bcbs = extracted_mappings.bcbs_with_ordinary_code_mappings();
-    for (bcb, expression_id) in coverage_counters
-        .bcb_nodes_with_coverage_expressions()
-        .filter(|&(bcb, _)| eligible_bcbs.contains(bcb))
-    {
-        inject_statement(
-            mir_body,
-            CoverageKind::ExpressionUsed { id: expression_id },
-            graph[bcb].leader_bb(),
-        );
+/// Inject any necessary coverage statements into MIR, so that they influence codegen.
+fn inject_coverage_statements<'tcx>(mir_body: &mut mir::Body<'tcx>, graph: &CoverageGraph) {
+    for (bcb, data) in graph.iter_enumerated() {
+        let target_bb = data.leader_bb();
+        inject_statement(mir_body, CoverageKind::VirtualCounter { bcb }, target_bb);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index a849ed4c3e2..cd89fbe772d 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -1,10 +1,7 @@
 use rustc_data_structures::captures::Captures;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::mir::coverage::{
-    CounterId, CovTerm, CoverageIdsInfo, CoverageKind, Expression, ExpressionId,
-    FunctionCoverageInfo, MappingKind, Op,
-};
+use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind};
 use rustc_middle::mir::{Body, Statement, StatementKind};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::util::Providers;
@@ -12,6 +9,9 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::sym;
 use tracing::trace;
 
+use crate::coverage::counters::node_flow::make_node_counters;
+use crate::coverage::counters::{CoverageCounters, transcribe_counters};
+
 /// Registers query/hook implementations related to coverage.
 pub(crate) fn provide(providers: &mut Providers) {
     providers.hooks.is_eligible_for_coverage = is_eligible_for_coverage;
@@ -89,39 +89,57 @@ fn coverage_ids_info<'tcx>(
     let mir_body = tcx.instance_mir(instance_def);
     let fn_cov_info = mir_body.function_coverage_info.as_deref()?;
 
-    let mut counters_seen = DenseBitSet::new_empty(fn_cov_info.num_counters);
-    let mut expressions_seen = DenseBitSet::new_filled(fn_cov_info.expressions.len());
-
-    // For each expression ID that is directly used by one or more mappings,
-    // mark it as not-yet-seen. This indicates that we expect to see a
-    // corresponding `ExpressionUsed` statement during MIR traversal.
-    for mapping in fn_cov_info.mappings.iter() {
-        // Currently we only worry about ordinary code mappings.
-        // For branch and MC/DC mappings, expressions might not correspond
-        // to any particular point in the control-flow graph.
-        // (Keep this in sync with the injection of `ExpressionUsed`
-        // statements in the `InstrumentCoverage` MIR pass.)
-        if let MappingKind::Code(CovTerm::Expression(id)) = mapping.kind {
-            expressions_seen.remove(id);
-        }
-    }
-
+    // Scan through the final MIR to see which BCBs survived MIR opts.
+    // Any BCB not in this set was optimized away.
+    let mut bcbs_seen = DenseBitSet::new_empty(fn_cov_info.priority_list.len());
     for kind in all_coverage_in_mir_body(mir_body) {
         match *kind {
-            CoverageKind::CounterIncrement { id } => {
-                counters_seen.insert(id);
-            }
-            CoverageKind::ExpressionUsed { id } => {
-                expressions_seen.insert(id);
+            CoverageKind::VirtualCounter { bcb } => {
+                bcbs_seen.insert(bcb);
             }
             _ => {}
         }
     }
 
-    let zero_expressions =
-        identify_zero_expressions(fn_cov_info, &counters_seen, &expressions_seen);
+    // Determine the set of BCBs that are referred to by mappings, and therefore
+    // need a counter. Any node not in this set will only get a counter if it
+    // is part of the counter expression for a node that is in the set.
+    let mut bcb_needs_counter =
+        DenseBitSet::<BasicCoverageBlock>::new_empty(fn_cov_info.priority_list.len());
+    for mapping in &fn_cov_info.mappings {
+        match mapping.kind {
+            MappingKind::Code { bcb } => {
+                bcb_needs_counter.insert(bcb);
+            }
+            MappingKind::Branch { true_bcb, false_bcb } => {
+                bcb_needs_counter.insert(true_bcb);
+                bcb_needs_counter.insert(false_bcb);
+            }
+            MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params: _ } => {
+                bcb_needs_counter.insert(true_bcb);
+                bcb_needs_counter.insert(false_bcb);
+            }
+            MappingKind::MCDCDecision(_) => {}
+        }
+    }
 
-    Some(CoverageIdsInfo { counters_seen, zero_expressions })
+    // FIXME(Zalathar): It should be possible to sort `priority_list[1..]` by
+    // `!bcbs_seen.contains(bcb)` to simplify the mappings even further, at the
+    // expense of some churn in the tests. When doing so, also consider removing
+    // the sorts in `transcribe_counters`.
+    let node_counters = make_node_counters(&fn_cov_info.node_flow_data, &fn_cov_info.priority_list);
+    let coverage_counters = transcribe_counters(&node_counters, &bcb_needs_counter, &bcbs_seen);
+
+    let CoverageCounters {
+        phys_counter_for_node, next_counter_id, node_counters, expressions, ..
+    } = coverage_counters;
+
+    Some(CoverageIdsInfo {
+        num_counters: next_counter_id.as_u32(),
+        phys_counter_for_node,
+        term_for_bcb: node_counters,
+        expressions,
+    })
 }
 
 fn all_coverage_in_mir_body<'a, 'tcx>(
@@ -139,94 +157,3 @@ fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool {
     let scope_data = &body.source_scopes[statement.source_info.scope];
     scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some()
 }
-
-/// Identify expressions that will always have a value of zero, and note their
-/// IDs in a `DenseBitSet`. Mappings that refer to a zero expression can instead
-/// become mappings to a constant zero value.
-///
-/// This function mainly exists to preserve the simplifications that were
-/// already being performed by the Rust-side expression renumbering, so that
-/// the resulting coverage mappings don't get worse.
-fn identify_zero_expressions(
-    fn_cov_info: &FunctionCoverageInfo,
-    counters_seen: &DenseBitSet<CounterId>,
-    expressions_seen: &DenseBitSet<ExpressionId>,
-) -> DenseBitSet<ExpressionId> {
-    // The set of expressions that either were optimized out entirely, or
-    // have zero as both of their operands, and will therefore always have
-    // a value of zero. Other expressions that refer to these as operands
-    // can have those operands replaced with `CovTerm::Zero`.
-    let mut zero_expressions = DenseBitSet::new_empty(fn_cov_info.expressions.len());
-
-    // Simplify a copy of each expression based on lower-numbered expressions,
-    // and then update the set of always-zero expressions if necessary.
-    // (By construction, expressions can only refer to other expressions
-    // that have lower IDs, so one pass is sufficient.)
-    for (id, expression) in fn_cov_info.expressions.iter_enumerated() {
-        if !expressions_seen.contains(id) {
-            // If an expression was not seen, it must have been optimized away,
-            // so any operand that refers to it can be replaced with zero.
-            zero_expressions.insert(id);
-            continue;
-        }
-
-        // We don't need to simplify the actual expression data in the
-        // expressions list; we can just simplify a temporary copy and then
-        // use that to update the set of always-zero expressions.
-        let Expression { mut lhs, op, mut rhs } = *expression;
-
-        // If an expression has an operand that is also an expression, the
-        // operand's ID must be strictly lower. This is what lets us find
-        // all zero expressions in one pass.
-        let assert_operand_expression_is_lower = |operand_id: ExpressionId| {
-            assert!(
-                operand_id < id,
-                "Operand {operand_id:?} should be less than {id:?} in {expression:?}",
-            )
-        };
-
-        // If an operand refers to a counter or expression that is always
-        // zero, then that operand can be replaced with `CovTerm::Zero`.
-        let maybe_set_operand_to_zero = |operand: &mut CovTerm| {
-            if let CovTerm::Expression(id) = *operand {
-                assert_operand_expression_is_lower(id);
-            }
-
-            if is_zero_term(&counters_seen, &zero_expressions, *operand) {
-                *operand = CovTerm::Zero;
-            }
-        };
-        maybe_set_operand_to_zero(&mut lhs);
-        maybe_set_operand_to_zero(&mut rhs);
-
-        // Coverage counter values cannot be negative, so if an expression
-        // involves subtraction from zero, assume that its RHS must also be zero.
-        // (Do this after simplifications that could set the LHS to zero.)
-        if lhs == CovTerm::Zero && op == Op::Subtract {
-            rhs = CovTerm::Zero;
-        }
-
-        // After the above simplifications, if both operands are zero, then
-        // we know that this expression is always zero too.
-        if lhs == CovTerm::Zero && rhs == CovTerm::Zero {
-            zero_expressions.insert(id);
-        }
-    }
-
-    zero_expressions
-}
-
-/// Returns `true` if the given term is known to have a value of zero, taking
-/// into account knowledge of which counters are unused and which expressions
-/// are always zero.
-fn is_zero_term(
-    counters_seen: &DenseBitSet<CounterId>,
-    zero_expressions: &DenseBitSet<ExpressionId>,
-    term: CovTerm,
-) -> bool {
-    match term {
-        CovTerm::Zero => true,
-        CovTerm::Counter(id) => !counters_seen.contains(id),
-        CovTerm::Expression(id) => zero_expressions.contains(id),
-    }
-}
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 26ce743be36..73b68d7155c 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -137,8 +137,7 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
 
         // These coverage statements should not exist prior to coverage instrumentation.
         StatementKind::Coverage(
-            CoverageKind::CounterIncrement { .. }
-            | CoverageKind::ExpressionUsed { .. }
+            CoverageKind::VirtualCounter { .. }
             | CoverageKind::CondBitmapUpdate { .. }
             | CoverageKind::TestVectorBitmapUpdate { .. },
         ) => bug!(
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml
index 6e8fd32610b..8b0cc9d726b 100644
--- a/compiler/rustc_query_impl/Cargo.toml
+++ b/compiler/rustc_query_impl/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-field-offset = "0.3.5"
 measureme = "11"
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index bbc83cca99d..73c205fdb17 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -11,7 +11,6 @@
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
-use field_offset::offset_of;
 use rustc_data_structures::stable_hasher::HashStable;
 use rustc_data_structures::sync::AtomicU64;
 use rustc_middle::arena::Arena;
@@ -89,7 +88,13 @@ where
     where
         QueryCtxt<'tcx>: 'a,
     {
-        self.dynamic.query_state.apply(&qcx.tcx.query_system.states)
+        // Safety:
+        // This is just manually doing the subfield referencing through pointer math.
+        unsafe {
+            &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>)
+                .byte_add(self.dynamic.query_state)
+                .cast::<QueryState<Self::Key>>()
+        }
     }
 
     #[inline(always)]
@@ -97,7 +102,13 @@ where
     where
         'tcx: 'a,
     {
-        self.dynamic.query_cache.apply(&qcx.tcx.query_system.caches)
+        // Safety:
+        // This is just manually doing the subfield referencing through pointer math.
+        unsafe {
+            &*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>)
+                .byte_add(self.dynamic.query_cache)
+                .cast::<Self::Cache>()
+        }
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 46ec538735a..e95c186f6b6 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -605,8 +605,8 @@ macro_rules! define_queries {
                     eval_always: is_eval_always!([$($modifiers)*]),
                     dep_kind: dep_graph::dep_kinds::$name,
                     handle_cycle_error: handle_cycle_error!([$($modifiers)*]),
-                    query_state: offset_of!(QueryStates<'tcx> => $name),
-                    query_cache: offset_of!(QueryCaches<'tcx> => $name),
+                    query_state: std::mem::offset_of!(QueryStates<'tcx>, $name),
+                    query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name),
                     cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key),
                     execute_query: |tcx, key| erase(tcx.$name(key)),
                     compute: |tcx, key| {
diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs
index e9b7f985667..e686a02f29b 100644
--- a/library/alloc/src/alloc.rs
+++ b/library/alloc/src/alloc.rs
@@ -10,7 +10,7 @@ use core::hint;
 #[cfg(not(test))]
 use core::ptr::{self, NonNull};
 
-extern "Rust" {
+unsafe extern "Rust" {
     // These are the magic symbols to call the global allocator. rustc generates
     // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute
     // (the code expanding that attribute macro generates those functions), or to call
@@ -355,7 +355,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
 // # Allocation error handler
 
 #[cfg(not(no_global_oom_handling))]
-extern "Rust" {
+unsafe extern "Rust" {
     // This is the magic symbol to call the global alloc error handler. rustc generates
     // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the
     // default implementations below (`__rdl_oom`) otherwise.
@@ -426,7 +426,7 @@ pub mod __alloc_error_handler {
     // `#[alloc_error_handler]`.
     #[rustc_std_internal_symbol]
     pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! {
-        extern "Rust" {
+        unsafe extern "Rust" {
             // This symbol is emitted by rustc next to __rust_alloc_error_handler.
             // Its value depends on the -Zoom={panic,abort} compiler option.
             static __rust_alloc_error_handler_should_panic: u8;
diff --git a/library/alloc/src/collections/btree/merge_iter.rs b/library/alloc/src/collections/btree/merge_iter.rs
index c5b93d30a11..5077062e25d 100644
--- a/library/alloc/src/collections/btree/merge_iter.rs
+++ b/library/alloc/src/collections/btree/merge_iter.rs
@@ -74,7 +74,7 @@ impl<I: Iterator> MergeIterInner<I> {
                 b_next = self.b.next();
             }
         }
-        if let (Some(ref a1), Some(ref b1)) = (&a_next, &b_next) {
+        if let (Some(a1), Some(b1)) = (&a_next, &b_next) {
             match cmp(a1, b1) {
                 Ordering::Less => self.peeked = b_next.take().map(Peeked::B),
                 Ordering::Greater => self.peeked = a_next.take().map(Peeked::A),
diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs
index 07c75677d05..fd93045a5ac 100644
--- a/library/alloc/src/ffi/c_str.rs
+++ b/library/alloc/src/ffi/c_str.rs
@@ -397,7 +397,7 @@ impl CString {
         // information about the size of the allocation is correct on Rust's
         // side.
         unsafe {
-            extern "C" {
+            unsafe extern "C" {
                 /// Provided by libc or compiler_builtins.
                 fn strlen(s: *const c_char) -> usize;
             }
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index 6d82f514aa6..09206c2f8b2 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -1462,18 +1462,18 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> {
     /// Provides a raw pointer to the data.
     ///
     /// The counts are not affected in any way and the `Rc` is not consumed. The pointer is valid
-    /// for as long there are strong counts in the `Rc`.
+    /// for as long as there are strong counts in the `Rc`.
     ///
     /// # Examples
     ///
     /// ```
     /// use std::rc::Rc;
     ///
-    /// let x = Rc::new("hello".to_owned());
+    /// let x = Rc::new(0);
     /// let y = Rc::clone(&x);
     /// let x_ptr = Rc::as_ptr(&x);
     /// assert_eq!(x_ptr, Rc::as_ptr(&y));
-    /// assert_eq!(unsafe { &*x_ptr }, "hello");
+    /// assert_eq!(unsafe { *x_ptr }, 0);
     /// ```
     #[stable(feature = "weak_into_raw", since = "1.45.0")]
     #[rustc_never_returns_null_ptr]
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index 7180593edf0..75338e492ee 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -731,7 +731,7 @@ const unsafe fn strlen(ptr: *const c_char) -> usize {
 
             len
         } else {
-            extern "C" {
+            unsafe extern "C" {
                 /// Provided by libc or compiler_builtins.
                 fn strlen(s: *const c_char) -> usize;
             }
diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs
index 50968c57adc..9bae5fd466a 100644
--- a/library/core/src/ffi/mod.rs
+++ b/library/core/src/ffi/mod.rs
@@ -90,4 +90,4 @@ impl fmt::Debug for c_void {
     cfg(not(target_feature = "crt-static"))
 )]
 #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))]
-extern "C" {}
+unsafe extern "C" {}
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index 3b249c835f2..6c9c6d0edc2 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -4855,7 +4855,7 @@ pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 {
 #[cfg(miri)]
 #[rustc_allow_const_fn_unstable(const_eval_select)]
 pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize) {
-    extern "Rust" {
+    unsafe extern "Rust" {
         /// Miri-provided extern function to promise that a given pointer is properly aligned for
         /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is
         /// not a power of two. Has no effect when alignment checks are concrete (which is the default).
diff --git a/library/core/src/iter/sources/once_with.rs b/library/core/src/iter/sources/once_with.rs
index 8b31ab2ff90..c9698b4fd43 100644
--- a/library/core/src/iter/sources/once_with.rs
+++ b/library/core/src/iter/sources/once_with.rs
@@ -58,8 +58,8 @@ use crate::iter::{FusedIterator, TrustedLen};
 /// ```
 #[inline]
 #[stable(feature = "iter_once_with", since = "1.43.0")]
-pub fn once_with<A, F: FnOnce() -> A>(gen: F) -> OnceWith<F> {
-    OnceWith { gen: Some(gen) }
+pub fn once_with<A, F: FnOnce() -> A>(make: F) -> OnceWith<F> {
+    OnceWith { make: Some(make) }
 }
 
 /// An iterator that yields a single element of type `A` by
@@ -70,13 +70,13 @@ pub fn once_with<A, F: FnOnce() -> A>(gen: F) -> OnceWith<F> {
 #[derive(Clone)]
 #[stable(feature = "iter_once_with", since = "1.43.0")]
 pub struct OnceWith<F> {
-    gen: Option<F>,
+    make: Option<F>,
 }
 
 #[stable(feature = "iter_once_with_debug", since = "1.68.0")]
 impl<F> fmt::Debug for OnceWith<F> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.gen.is_some() {
+        if self.make.is_some() {
             f.write_str("OnceWith(Some(_))")
         } else {
             f.write_str("OnceWith(None)")
@@ -90,13 +90,13 @@ impl<A, F: FnOnce() -> A> Iterator for OnceWith<F> {
 
     #[inline]
     fn next(&mut self) -> Option<A> {
-        let f = self.gen.take()?;
+        let f = self.make.take()?;
         Some(f())
     }
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        self.gen.iter().size_hint()
+        self.make.iter().size_hint()
     }
 }
 
@@ -110,7 +110,7 @@ impl<A, F: FnOnce() -> A> DoubleEndedIterator for OnceWith<F> {
 #[stable(feature = "iter_once_with", since = "1.43.0")]
 impl<A, F: FnOnce() -> A> ExactSizeIterator for OnceWith<F> {
     fn len(&self) -> usize {
-        self.gen.iter().len()
+        self.make.iter().len()
     }
 }
 
diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs
index b97f19e1baa..d36e677d21a 100644
--- a/library/core/src/panicking.rs
+++ b/library/core/src/panicking.rs
@@ -59,7 +59,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
 
     // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
     // that gets resolved to the `#[panic_handler]` function.
-    extern "Rust" {
+    unsafe extern "Rust" {
         #[lang = "panic_impl"]
         fn panic_impl(pi: &PanicInfo<'_>) -> !;
     }
@@ -100,7 +100,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo
 
             // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
             // that gets resolved to the `#[panic_handler]` function.
-            extern "Rust" {
+            unsafe extern "Rust" {
                 #[lang = "panic_impl"]
                 fn panic_impl(pi: &PanicInfo<'_>) -> !;
             }
diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs
index e93b5658e24..9eee29d485f 100644
--- a/library/core/src/ptr/metadata.rs
+++ b/library/core/src/ptr/metadata.rs
@@ -155,7 +155,7 @@ pub struct DynMetadata<Dyn: ?Sized> {
     _phantom: crate::marker::PhantomData<Dyn>,
 }
 
-extern "C" {
+unsafe extern "C" {
     /// Opaque type for accessing vtables.
     ///
     /// Private implementation detail of `DynMetadata::size_of` etc.
diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs
index 1b5c5fc82a6..9cb94ca3b0f 100644
--- a/library/coretests/tests/mem.rs
+++ b/library/coretests/tests/mem.rs
@@ -648,7 +648,7 @@ fn offset_of_dst() {
         z: dyn Trait,
     }
 
-    extern "C" {
+    unsafe extern "C" {
         type Extern;
     }
 
diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs
index 3d825224813..6041923117c 100644
--- a/library/coretests/tests/num/flt2dec/mod.rs
+++ b/library/coretests/tests/num/flt2dec/mod.rs
@@ -80,7 +80,7 @@ fn ldexp_f32(a: f32, b: i32) -> f32 {
 }
 
 fn ldexp_f64(a: f64, b: i32) -> f64 {
-    extern "C" {
+    unsafe extern "C" {
         fn ldexp(x: f64, n: i32) -> f64;
     }
     // SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly
diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs
index 345bec345d1..0c9f9b338b0 100644
--- a/library/coretests/tests/ptr.rs
+++ b/library/coretests/tests/ptr.rs
@@ -97,7 +97,7 @@ fn test_is_null() {
     let nmi: *mut dyn ToString = null_mut::<isize>();
     assert!(nmi.is_null());
 
-    extern "C" {
+    unsafe extern "C" {
         type Extern;
     }
     let ec: *const Extern = null::<Extern>();
@@ -308,7 +308,7 @@ fn test_const_nonnull_new() {
 pub fn test_variadic_fnptr() {
     use core::ffi;
     use core::hash::{Hash, SipHasher};
-    extern "C" {
+    unsafe extern "C" {
         // This needs to use the correct function signature even though it isn't called as some
         // codegen backends make it UB to declare a function with multiple conflicting signatures
         // (like LLVM) while others straight up return an error (like Cranelift).
@@ -506,7 +506,7 @@ fn offset_from() {
 fn ptr_metadata() {
     struct Unit;
     struct Pair<A, B: ?Sized>(A, B);
-    extern "C" {
+    unsafe extern "C" {
         type Extern;
     }
     let () = metadata(&());
diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs
index dc2b42bb90a..7718d68aef8 100644
--- a/library/panic_abort/src/lib.rs
+++ b/library/panic_abort/src/lib.rs
@@ -54,7 +54,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
         ))] {
             unsafe fn abort() -> ! {
                 // call std::sys::abort_internal
-                extern "C" {
+                unsafe extern "C" {
                     pub fn __rust_abort() -> !;
                 }
                 __rust_abort();
@@ -87,7 +87,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
             }
         } else if #[cfg(target_os = "teeos")] {
             mod teeos {
-                extern "C" {
+                unsafe extern "C" {
                     pub fn TEE_Panic(code: u32) -> !;
                 }
             }
diff --git a/library/panic_abort/src/zkvm.rs b/library/panic_abort/src/zkvm.rs
index a6a02abf109..11150eafd0b 100644
--- a/library/panic_abort/src/zkvm.rs
+++ b/library/panic_abort/src/zkvm.rs
@@ -16,7 +16,7 @@ pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) {
         return;
     }
 
-    extern "C" {
+    unsafe extern "C" {
         fn sys_panic(msg_ptr: *const u8, len: usize) -> !;
     }
 
diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs
index 86906b46d66..4140b004ad1 100644
--- a/library/panic_unwind/src/emcc.rs
+++ b/library/panic_unwind/src/emcc.rs
@@ -21,7 +21,7 @@ struct TypeInfo {
 }
 unsafe impl Sync for TypeInfo {}
 
-extern "C" {
+unsafe extern "C" {
     // The leading `\x01` byte here is actually a magical signal to LLVM to
     // *not* apply any other mangling like prefixing with a `_` character.
     //
@@ -119,7 +119,7 @@ extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void {
     }
 }
 
-extern "C" {
+unsafe extern "C" {
     fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void;
     fn __cxa_begin_catch(thrown_exception: *mut libc::c_void) -> *mut libc::c_void;
     fn __cxa_end_catch();
diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs
index 7e08ad66577..9719c133415 100644
--- a/library/panic_unwind/src/hermit.rs
+++ b/library/panic_unwind/src/hermit.rs
@@ -6,14 +6,14 @@ use alloc::boxed::Box;
 use core::any::Any;
 
 pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> {
-    extern "C" {
+    unsafe extern "C" {
         fn __rust_abort() -> !;
     }
     __rust_abort();
 }
 
 pub(crate) unsafe fn panic(_data: Box<dyn Any + Send>) -> u32 {
-    extern "C" {
+    unsafe extern "C" {
         fn __rust_abort() -> !;
     }
     __rust_abort();
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index d6828164195..45e2a466b4d 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -75,7 +75,7 @@ cfg_if::cfg_if! {
     }
 }
 
-extern "C" {
+unsafe extern "C" {
     /// Handler in std called when a panic object is dropped outside of
     /// `catch_unwind`.
     fn __rust_drop_panic() -> !;
diff --git a/library/panic_unwind/src/miri.rs b/library/panic_unwind/src/miri.rs
index a86f0e91eef..ec48b1105ab 100644
--- a/library/panic_unwind/src/miri.rs
+++ b/library/panic_unwind/src/miri.rs
@@ -7,7 +7,7 @@ use core::any::Any;
 // Must be pointer-sized.
 type Payload = Box<Box<dyn Any + Send>>;
 
-extern "Rust" {
+unsafe extern "Rust" {
     /// Miri-provided extern function to begin unwinding.
     fn miri_start_unwind(payload: *mut u8) -> !;
 }
diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs
index 21bfe74e1a2..c8dfddf821e 100644
--- a/library/panic_unwind/src/seh.rs
+++ b/library/panic_unwind/src/seh.rs
@@ -135,7 +135,7 @@ mod imp {
     #[derive(Copy, Clone)]
     pub(super) struct ptr_t(u32);
 
-    extern "C" {
+    unsafe extern "C" {
         static __ImageBase: u8;
     }
 
@@ -229,7 +229,7 @@ static mut CATCHABLE_TYPE: _CatchableType = _CatchableType {
     copyFunction: ptr_t::null(),
 };
 
-extern "C" {
+unsafe extern "C" {
     // The leading `\x01` byte here is actually a magical signal to LLVM to
     // *not* apply any other mangling like prefixing with a `_` character.
     //
@@ -343,7 +343,7 @@ pub(crate) unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
         ptr_t::new(exception_copy as *mut u8).raw(),
     );
 
-    extern "system-unwind" {
+    unsafe extern "system-unwind" {
         fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
     }
 
diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs
index d3ff5c14aa4..67b09599d9d 100644
--- a/library/rtstartup/rsbegin.rs
+++ b/library/rtstartup/rsbegin.rs
@@ -52,7 +52,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
 #[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))]
 pub mod eh_frames {
     #[no_mangle]
-    #[link_section = ".eh_frame"]
+    #[unsafe(link_section = ".eh_frame")]
     // Marks beginning of the stack frame unwind info section
     pub static __EH_FRAME_BEGIN__: [u8; 0] = [];
 
@@ -76,7 +76,7 @@ pub mod eh_frames {
     }
 
     // Unwind info registration/deregistration routines.
-    extern "C" {
+    unsafe extern "C" {
         fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8);
         fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
     }
@@ -101,10 +101,10 @@ pub mod eh_frames {
         // end of the list. Since constructors are run in reverse order, this ensures that our
         // callbacks are the first and last ones executed.
 
-        #[link_section = ".ctors.65535"] // .ctors.* : C initialization callbacks
+        #[unsafe(link_section = ".ctors.65535")] // .ctors.* : C initialization callbacks
         pub static P_INIT: unsafe extern "C" fn() = super::init;
 
-        #[link_section = ".dtors.65535"] // .dtors.* : C termination callbacks
+        #[unsafe(link_section = ".dtors.65535")] // .dtors.* : C termination callbacks
         pub static P_UNINIT: unsafe extern "C" fn() = super::uninit;
     }
 }
diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs
index 81acfbed447..a6f7d103356 100644
--- a/library/rtstartup/rsend.rs
+++ b/library/rtstartup/rsend.rs
@@ -32,6 +32,6 @@ pub mod eh_frames {
     // Terminate the frame unwind info section with a 0 as a sentinel;
     // this would be the 'length' field in a real FDE.
     #[no_mangle]
-    #[link_section = ".eh_frame"]
+    #[unsafe(link_section = ".eh_frame")]
     pub static __EH_FRAME_END__: u32 = 0;
 }
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs
index 5d51d6a0c78..3936ed057e6 100644
--- a/library/std/src/alloc.rs
+++ b/library/std/src/alloc.rs
@@ -345,7 +345,7 @@ pub fn take_alloc_error_hook() -> fn(Layout) {
 }
 
 fn default_alloc_error_hook(layout: Layout) {
-    extern "Rust" {
+    unsafe extern "Rust" {
         // This symbol is emitted by rustc next to __rust_alloc_error_handler.
         // Its value depends on the -Zoom={panic,abort} compiler option.
         static __rust_alloc_error_handler_should_panic: u8;
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 8e50bf11dd0..b47b41d4bc5 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -54,11 +54,11 @@ pub static EMPTY_PANIC: fn(&'static str) -> ! =
 // One day this may look a little less ad-hoc with the compiler helping out to
 // hook up these functions, but it is not this day!
 #[allow(improper_ctypes)]
-extern "C" {
+unsafe extern "C" {
     fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
 }
 
-extern "Rust" {
+unsafe extern "Rust" {
     /// `PanicPayload` lazily performs allocation only when needed (this avoids
     /// allocations when using the "abort" panic runtime).
     fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32;
diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs
index 00966ee3ecf..8caa2dcfad9 100644
--- a/library/std/src/sync/mpmc/mod.rs
+++ b/library/std/src/sync/mpmc/mod.rs
@@ -616,9 +616,9 @@ impl<T> Sender<T> {
     #[unstable(feature = "mpmc_channel", issue = "126840")]
     pub fn same_channel(&self, other: &Sender<T>) -> bool {
         match (&self.flavor, &other.flavor) {
-            (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b,
-            (SenderFlavor::List(ref a), SenderFlavor::List(ref b)) => a == b,
-            (SenderFlavor::Zero(ref a), SenderFlavor::Zero(ref b)) => a == b,
+            (SenderFlavor::Array(a), SenderFlavor::Array(b)) => a == b,
+            (SenderFlavor::List(a), SenderFlavor::List(b)) => a == b,
+            (SenderFlavor::Zero(a), SenderFlavor::Zero(b)) => a == b,
             _ => false,
         }
     }
diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs
index 321d30e0b11..9d22e16fdf2 100644
--- a/library/std/src/sys/alloc/xous.rs
+++ b/library/std/src/sys/alloc/xous.rs
@@ -8,7 +8,7 @@ use crate::alloc::{GlobalAlloc, Layout, System};
 static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new();
 
 #[cfg(test)]
-extern "Rust" {
+unsafe extern "Rust" {
     #[link_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE"]
     static mut DLMALLOC: dlmalloc::Dlmalloc;
 }
diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs
index ee36127cfdf..e3f41f4fdbb 100644
--- a/library/std/src/sys/cmath.rs
+++ b/library/std/src/sys/cmath.rs
@@ -2,7 +2,7 @@
 
 // These symbols are all defined by `libm`,
 // or by `compiler-builtins` on unsupported platforms.
-extern "C" {
+unsafe extern "C" {
     pub fn acos(n: f64) -> f64;
     pub fn asin(n: f64) -> f64;
     pub fn atan(n: f64) -> f64;
diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs
index 3d555ad5050..a746b853cbd 100644
--- a/library/std/src/sys/pal/hermit/mod.rs
+++ b/library/std/src/sys/pal/hermit/mod.rs
@@ -78,7 +78,7 @@ pub unsafe extern "C" fn runtime_entry(
     argv: *const *const c_char,
     env: *const *const c_char,
 ) -> ! {
-    extern "C" {
+    unsafe extern "C" {
         fn main(argc: isize, argv: *const *const c_char) -> i32;
     }
 
diff --git a/library/std/src/sys/pal/itron/abi.rs b/library/std/src/sys/pal/itron/abi.rs
index 5eb14bb7e53..49b5251fc0e 100644
--- a/library/std/src/sys/pal/itron/abi.rs
+++ b/library/std/src/sys/pal/itron/abi.rs
@@ -132,7 +132,7 @@ pub struct T_CTSK {
     pub stk: *mut u8,
 }
 
-extern "C" {
+unsafe extern "C" {
     #[link_name = "__asp3_acre_tsk"]
     pub fn acre_tsk(pk_ctsk: *const T_CTSK) -> ER_ID;
     #[link_name = "__asp3_get_tid"]
diff --git a/library/std/src/sys/pal/sgx/abi/mem.rs b/library/std/src/sys/pal/sgx/abi/mem.rs
index 18e6d5b3fa2..e6ce15bed3c 100644
--- a/library/std/src/sys/pal/sgx/abi/mem.rs
+++ b/library/std/src/sys/pal/sgx/abi/mem.rs
@@ -12,7 +12,7 @@ pub(crate) unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T {
     (image_base() + offset) as *mut T
 }
 
-extern "C" {
+unsafe extern "C" {
     static ENCLAVE_SIZE: usize;
     static HEAP_BASE: u64;
     static HEAP_SIZE: usize;
diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs
index d8836452e75..f52acc41509 100644
--- a/library/std/src/sys/pal/sgx/abi/mod.rs
+++ b/library/std/src/sys/pal/sgx/abi/mod.rs
@@ -73,7 +73,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64
 
         EntryReturn(0, 0)
     } else {
-        extern "C" {
+        unsafe extern "C" {
             fn main(argc: isize, argv: *const *const u8) -> isize;
         }
 
diff --git a/library/std/src/sys/pal/sgx/abi/panic.rs b/library/std/src/sys/pal/sgx/abi/panic.rs
index c06b97ee367..67af062b0ff 100644
--- a/library/std/src/sys/pal/sgx/abi/panic.rs
+++ b/library/std/src/sys/pal/sgx/abi/panic.rs
@@ -2,7 +2,7 @@ use super::usercalls::alloc::UserRef;
 use crate::io::{self, Write};
 use crate::{cmp, mem};
 
-extern "C" {
+unsafe extern "C" {
     fn take_debug_panic_buf_ptr() -> *mut u8;
     static DEBUG: u8;
 }
diff --git a/library/std/src/sys/pal/sgx/abi/reloc.rs b/library/std/src/sys/pal/sgx/abi/reloc.rs
index 02dff0ad29f..a4f5e4a0936 100644
--- a/library/std/src/sys/pal/sgx/abi/reloc.rs
+++ b/library/std/src/sys/pal/sgx/abi/reloc.rs
@@ -11,7 +11,7 @@ struct Rela<T> {
 }
 
 pub fn relocate_elf_rela() {
-    extern "C" {
+    unsafe extern "C" {
         static RELA: u64;
         static RELACOUNT: usize;
     }
diff --git a/library/std/src/sys/pal/sgx/abi/thread.rs b/library/std/src/sys/pal/sgx/abi/thread.rs
index 2b23e368cc3..9b37e2baf36 100644
--- a/library/std/src/sys/pal/sgx/abi/thread.rs
+++ b/library/std/src/sys/pal/sgx/abi/thread.rs
@@ -6,7 +6,7 @@ use fortanix_sgx_abi::Tcs;
 /// is a one-to-one correspondence of the ID to the address of the TCS.
 #[unstable(feature = "sgx_platform", issue = "56975")]
 pub fn current() -> Tcs {
-    extern "C" {
+    unsafe extern "C" {
         fn get_tcs_addr() -> *mut u8;
     }
     let addr = unsafe { get_tcs_addr() };
diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs
index 34fc2f20d22..7131797003b 100644
--- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs
+++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs
@@ -22,7 +22,7 @@ macro_rules! dup {
 #[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"]
 static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0)));
 
-extern "C" {
+unsafe extern "C" {
     fn get_tls_ptr() -> *const u8;
     fn set_tls_ptr(tls: *const u8);
 }
diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs
index 943b771498f..28fbbc3c518 100644
--- a/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs
+++ b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs
@@ -9,7 +9,7 @@ use crate::ptr::NonNull;
 #[repr(C)]
 struct UsercallReturn(u64, u64);
 
-extern "C" {
+unsafe extern "C" {
     fn usercall(nr: NonZero<u64>, p1: u64, p2: u64, abort: u64, p3: u64, p4: u64)
     -> UsercallReturn;
 }
diff --git a/library/std/src/sys/pal/solid/abi/fs.rs b/library/std/src/sys/pal/solid/abi/fs.rs
index 6864a3e7745..7f2b1f83e85 100644
--- a/library/std/src/sys/pal/solid/abi/fs.rs
+++ b/library/std/src/sys/pal/solid/abi/fs.rs
@@ -31,7 +31,7 @@ pub const DT_WHT: c_uchar = 14;
 
 pub type S_DIR = c_int;
 
-extern "C" {
+unsafe extern "C" {
     pub fn SOLID_FS_Open(fd: *mut c_int, path: *const c_char, mode: c_int) -> c_int;
     pub fn SOLID_FS_Close(fd: c_int) -> c_int;
     pub fn SOLID_FS_Read(fd: c_int, buf: *mut u8, size: usize, result: *mut usize) -> c_int;
diff --git a/library/std/src/sys/pal/solid/abi/mod.rs b/library/std/src/sys/pal/solid/abi/mod.rs
index 4d097057217..819f93f4074 100644
--- a/library/std/src/sys/pal/solid/abi/mod.rs
+++ b/library/std/src/sys/pal/solid/abi/mod.rs
@@ -33,27 +33,27 @@ pub struct SOLID_RTC_TIME {
     pub tm_wday: c_int,
 }
 
-extern "C" {
+unsafe extern "C" {
     pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int;
 }
 
 // `solid_log.h`
-extern "C" {
+unsafe extern "C" {
     pub fn SOLID_LOG_write(s: *const u8, l: usize);
 }
 
 // `solid_mem.h`
-extern "C" {
+unsafe extern "C" {
     pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8));
 }
 
 // `solid_rng.h`
-extern "C" {
+unsafe extern "C" {
     pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int;
 }
 
 // `rwlock.h`
-extern "C" {
+unsafe extern "C" {
     pub fn rwl_loc_rdl(id: ID) -> ER;
     pub fn rwl_loc_wrl(id: ID) -> ER;
     pub fn rwl_ploc_rdl(id: ID) -> ER;
diff --git a/library/std/src/sys/pal/solid/abi/sockets.rs b/library/std/src/sys/pal/solid/abi/sockets.rs
index 3c9e3f9ffb9..80802dd42e2 100644
--- a/library/std/src/sys/pal/solid/abi/sockets.rs
+++ b/library/std/src/sys/pal/solid/abi/sockets.rs
@@ -158,7 +158,7 @@ pub struct fd_set {
     pub fds: [c_int; SOLID_NET_FD_SETSIZE],
 }
 
-extern "C" {
+unsafe extern "C" {
     #[link_name = "SOLID_NET_StrError"]
     pub fn strerror(errnum: c_int) -> *const c_char;
 
diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs
index 57c28aed3b2..e3b2e0aa50f 100644
--- a/library/std/src/sys/pal/solid/os.rs
+++ b/library/std/src/sys/pal/solid/os.rs
@@ -129,7 +129,7 @@ impl Iterator for Env {
 /// Returns a vector of (variable, value) byte-vector pairs for all the
 /// environment variables of the current process.
 pub fn env() -> Env {
-    extern "C" {
+    unsafe extern "C" {
         static mut environ: *const *const c_char;
     }
 
diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs
index 15c65240ddd..c779c5f3ed8 100644
--- a/library/std/src/sys/pal/teeos/thread.rs
+++ b/library/std/src/sys/pal/teeos/thread.rs
@@ -16,7 +16,7 @@ pub struct Thread {
 unsafe impl Send for Thread {}
 unsafe impl Sync for Thread {}
 
-extern "C" {
+unsafe extern "C" {
     pub fn TEE_Wait(timeout: u32) -> u32;
 }
 
diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs
index 8438a61e90f..1c87a79803c 100644
--- a/library/std/src/sys/pal/unix/args.rs
+++ b/library/std/src/sys/pal/unix/args.rs
@@ -147,7 +147,7 @@ mod imp {
     /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
     #[cfg(all(target_os = "linux", target_env = "gnu"))]
     #[used]
-    #[link_section = ".init_array.00099"]
+    #[unsafe(link_section = ".init_array.00099")]
     static ARGV_INIT_ARRAY: extern "C" fn(
         crate::os::raw::c_int,
         *const *const u8,
@@ -204,7 +204,7 @@ mod imp {
     }
 
     pub fn argc_argv() -> (isize, *const *const c_char) {
-        extern "C" {
+        unsafe extern "C" {
             // These functions are in crt_externs.h.
             fn _NSGetArgc() -> *mut c_int;
             fn _NSGetArgv() -> *mut *mut *mut c_char;
diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs
index 0fc765dc87a..d4551dd6a38 100644
--- a/library/std/src/sys/pal/unix/futex.rs
+++ b/library/std/src/sys/pal/unix/futex.rs
@@ -219,7 +219,7 @@ pub fn futex_wake_all(futex: &AtomicU32) {
 }
 
 #[cfg(target_os = "emscripten")]
-extern "C" {
+unsafe extern "C" {
     fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
     fn emscripten_futex_wait(
         addr: *const AtomicU32,
@@ -267,7 +267,7 @@ pub mod zircon {
     pub const ZX_ERR_BAD_STATE: zx_status_t = -20;
     pub const ZX_ERR_TIMED_OUT: zx_status_t = -21;
 
-    extern "C" {
+    unsafe extern "C" {
         pub fn zx_clock_get_monotonic() -> zx_time_t;
         pub fn zx_futex_wait(
             value_ptr: *const zx_futex_t,
diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs
index 027df6c5691..c0b56d8d2b2 100644
--- a/library/std/src/sys/pal/unix/mod.rs
+++ b/library/std/src/sys/pal/unix/mod.rs
@@ -373,24 +373,24 @@ cfg_if::cfg_if! {
             cfg(target_feature = "crt-static"))]
         #[link(name = "dl", cfg(not(target_feature = "crt-static")))]
         #[link(name = "log", cfg(not(target_feature = "crt-static")))]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(target_os = "freebsd")] {
         #[link(name = "execinfo")]
         #[link(name = "pthread")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(target_os = "netbsd")] {
         #[link(name = "pthread")]
         #[link(name = "rt")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] {
         #[link(name = "pthread")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(target_os = "solaris")] {
         #[link(name = "socket")]
         #[link(name = "posix4")]
         #[link(name = "pthread")]
         #[link(name = "resolv")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(target_os = "illumos")] {
         #[link(name = "socket")]
         #[link(name = "posix4")]
@@ -399,24 +399,24 @@ cfg_if::cfg_if! {
         #[link(name = "nsl")]
         // Use libumem for the (malloc-compatible) allocator
         #[link(name = "umem")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(target_vendor = "apple")] {
         // Link to `libSystem.dylib`.
         //
         // Don't get confused by the presence of `System.framework`,
         // it is a deprecated wrapper over the dynamic library.
         #[link(name = "System")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(target_os = "fuchsia")] {
         #[link(name = "zircon")]
         #[link(name = "fdio")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] {
         #[link(name = "dl")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(target_os = "vita")] {
         #[link(name = "pthread", kind = "static", modifiers = "-bundle")]
-        extern "C" {}
+        unsafe extern "C" {}
     }
 }
 
diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs
index b83772e34c1..04199c56330 100644
--- a/library/std/src/sys/pal/unix/os.rs
+++ b/library/std/src/sys/pal/unix/os.rs
@@ -30,7 +30,7 @@ cfg_if::cfg_if! {
     }
 }
 
-extern "C" {
+unsafe extern "C" {
     #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))]
     #[cfg_attr(
         any(
@@ -82,7 +82,7 @@ pub fn errno() -> i32 {
 
 #[cfg(target_os = "rtems")]
 pub fn errno() -> i32 {
-    extern "C" {
+    unsafe extern "C" {
         #[thread_local]
         static _tls_errno: c_int;
     }
@@ -92,7 +92,7 @@ pub fn errno() -> i32 {
 
 #[cfg(target_os = "dragonfly")]
 pub fn errno() -> i32 {
-    extern "C" {
+    unsafe extern "C" {
         #[thread_local]
         static errno: c_int;
     }
@@ -103,7 +103,7 @@ pub fn errno() -> i32 {
 #[cfg(target_os = "dragonfly")]
 #[allow(dead_code)]
 pub fn set_errno(e: i32) {
-    extern "C" {
+    unsafe extern "C" {
         #[thread_local]
         static mut errno: c_int;
     }
@@ -115,7 +115,7 @@ pub fn set_errno(e: i32) {
 
 /// Gets a detailed string description for the given error number.
 pub fn error_string(errno: i32) -> String {
-    extern "C" {
+    unsafe extern "C" {
         #[cfg_attr(
             all(
                 any(target_os = "linux", target_os = "hurd", target_env = "newlib"),
@@ -610,7 +610,7 @@ pub unsafe fn environ() -> *mut *const *const c_char {
 // Use the `environ` static which is part of POSIX.
 #[cfg(not(target_vendor = "apple"))]
 pub unsafe fn environ() -> *mut *const *const c_char {
-    extern "C" {
+    unsafe extern "C" {
         static mut environ: *const *const c_char;
     }
     &raw mut environ
@@ -847,7 +847,7 @@ pub fn getppid() -> u32 {
 
 #[cfg(all(target_os = "linux", target_env = "gnu"))]
 pub fn glibc_version() -> Option<(usize, usize)> {
-    extern "C" {
+    unsafe extern "C" {
         fn gnu_get_libc_version() -> *const libc::c_char;
     }
     let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
diff --git a/library/std/src/sys/pal/unix/process/zircon.rs b/library/std/src/sys/pal/unix/process/zircon.rs
index 4035e2370a3..7932bd26d76 100644
--- a/library/std/src/sys/pal/unix/process/zircon.rs
+++ b/library/std/src/sys/pal/unix/process/zircon.rs
@@ -75,7 +75,7 @@ pub struct zx_info_process_t {
     pub reserved1: u32,
 }
 
-extern "C" {
+unsafe extern "C" {
     pub fn zx_job_default() -> zx_handle_t;
 
     pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t;
@@ -115,7 +115,7 @@ pub struct fdio_spawn_action_t {
     pub reserved1: u64,
 }
 
-extern "C" {
+unsafe extern "C" {
     pub fn fdio_spawn_etc(
         job: zx_handle_t,
         flags: u32,
diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 356669980c7..479021af040 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -23,7 +23,7 @@ mod zircon {
     type zx_status_t = i32;
     pub const ZX_PROP_NAME: u32 = 3;
 
-    extern "C" {
+    unsafe extern "C" {
         pub fn zx_object_set_property(
             handle: zx_handle_t,
             property: u32,
@@ -230,7 +230,7 @@ impl Thread {
     #[cfg(target_os = "vxworks")]
     pub fn set_name(name: &CStr) {
         // FIXME(libc): adding real STATUS, ERROR type eventually.
-        extern "C" {
+        unsafe extern "C" {
             fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int;
         }
 
@@ -506,7 +506,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> {
         } else if #[cfg(target_os = "vxworks")] {
             // Note: there is also `vxCpuConfiguredGet`, closer to _SC_NPROCESSORS_CONF
             // expectations than the actual cores availability.
-            extern "C" {
+            unsafe extern "C" {
                 fn vxCpuEnabledGet() -> libc::cpuset_t;
             }
 
diff --git a/library/std/src/sys/pal/unix/thread_parking.rs b/library/std/src/sys/pal/unix/thread_parking.rs
index 72dd2031479..bef8b4fb363 100644
--- a/library/std/src/sys/pal/unix/thread_parking.rs
+++ b/library/std/src/sys/pal/unix/thread_parking.rs
@@ -8,7 +8,7 @@ use crate::ffi::{c_int, c_void};
 use crate::ptr;
 use crate::time::Duration;
 
-extern "C" {
+unsafe extern "C" {
     fn ___lwp_park60(
         clock_id: clockid_t,
         flags: c_int,
diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs
index 35762f5a53b..5a37598f438 100644
--- a/library/std/src/sys/pal/unix/weak.rs
+++ b/library/std/src/sys/pal/unix/weak.rs
@@ -31,7 +31,7 @@ use crate::{mem, ptr};
 pub(crate) macro weak {
     (fn $name:ident($($t:ty),*) -> $ret:ty) => (
         let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
-            extern "C" {
+            unsafe extern "C" {
                 #[linkage = "extern_weak"]
                 static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>;
             }
diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs
index 7779d2b97d7..7705e7f6b88 100644
--- a/library/std/src/sys/pal/wasi/fs.rs
+++ b/library/std/src/sys/pal/wasi/fs.rs
@@ -787,7 +787,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
             }
         }
 
-        extern "C" {
+        unsafe extern "C" {
             pub fn __wasilibc_find_relpath(
                 path: *const libc::c_char,
                 abs_prefix: *mut *const libc::c_char,
diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs
index f7701360f5a..ba2b65a1f40 100644
--- a/library/std/src/sys/pal/wasi/os.rs
+++ b/library/std/src/sys/pal/wasi/os.rs
@@ -16,7 +16,7 @@ use crate::{fmt, io, str, vec};
 mod libc {
     pub use libc::*;
 
-    extern "C" {
+    unsafe extern "C" {
         pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
         pub fn chdir(dir: *const c_char) -> c_int;
         pub fn __wasilibc_get_environ() -> *mut *mut c_char;
@@ -46,7 +46,7 @@ cfg_if::cfg_if! {
 }
 
 pub fn errno() -> i32 {
-    extern "C" {
+    unsafe extern "C" {
         #[thread_local]
         static errno: libc::c_int;
     }
diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs
index f5e19f26bfe..0ae02369410 100644
--- a/library/std/src/sys/pal/wasi/thread.rs
+++ b/library/std/src/sys/pal/wasi/thread.rs
@@ -35,7 +35,7 @@ cfg_if::cfg_if! {
 
             pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84;
 
-            extern "C" {
+            unsafe extern "C" {
                 pub fn pthread_create(
                     native: *mut pthread_t,
                     attr: *const pthread_attr_t,
diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs
index 9ce3e912caf..8dc61edb603 100644
--- a/library/std/src/sys/pal/windows/c.rs
+++ b/library/std/src/sys/pal/windows/c.rs
@@ -115,7 +115,7 @@ if #[cfg(not(target_vendor = "uwp"))] {
     link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated")
 )]
 #[cfg_attr(not(target_arch = "x86"), link(name = "bcryptprimitives", kind = "raw-dylib"))]
-extern "system" {
+unsafe extern "system" {
     pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL;
 }
 
@@ -164,7 +164,7 @@ compat_fn_with_fallback! {
     not(target_arch = "x86"),
     link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib")
 )]
-extern "system" {
+unsafe extern "system" {
     pub fn WaitOnAddress(
         address: *const c_void,
         compareaddress: *const c_void,
diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs
index 42999da1664..2b9838437e9 100644
--- a/library/std/src/sys/pal/windows/compat.rs
+++ b/library/std/src/sys/pal/windows/compat.rs
@@ -39,7 +39,7 @@ use crate::sys::c;
 // See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
 #[cfg(target_vendor = "win7")]
 #[used]
-#[link_section = ".CRT$XCT"]
+#[unsafe(link_section = ".CRT$XCT")]
 static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
 
 /// Preload some imported functions.
diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index bdb55643bb1..b786c79004f 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -44,7 +44,7 @@ pub struct FileType {
 }
 
 pub struct ReadDir {
-    handle: FindNextFileHandle,
+    handle: Option<FindNextFileHandle>,
     root: Arc<PathBuf>,
     first: Option<c::WIN32_FIND_DATAW>,
 }
@@ -113,13 +113,13 @@ impl fmt::Debug for ReadDir {
 impl Iterator for ReadDir {
     type Item = io::Result<DirEntry>;
     fn next(&mut self) -> Option<io::Result<DirEntry>> {
-        if self.handle.0 == c::INVALID_HANDLE_VALUE {
+        let Some(handle) = self.handle.as_ref() else {
             // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle.
             // Simply return `None` because this is only the case when `FindFirstFileExW` in
             // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means
             // no matchhing files can be found.
             return None;
-        }
+        };
         if let Some(first) = self.first.take() {
             if let Some(e) = DirEntry::new(&self.root, &first) {
                 return Some(Ok(e));
@@ -128,7 +128,7 @@ impl Iterator for ReadDir {
         unsafe {
             let mut wfd = mem::zeroed();
             loop {
-                if c::FindNextFileW(self.handle.0, &mut wfd) == 0 {
+                if c::FindNextFileW(handle.0, &mut wfd) == 0 {
                     match api::get_last_error() {
                         WinError::NO_MORE_FILES => return None,
                         WinError { code } => {
@@ -1194,7 +1194,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
 
         if find_handle != c::INVALID_HANDLE_VALUE {
             Ok(ReadDir {
-                handle: FindNextFileHandle(find_handle),
+                handle: Some(FindNextFileHandle(find_handle)),
                 root: Arc::new(root),
                 first: Some(wfd),
             })
@@ -1212,11 +1212,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
             // See issue #120040: https://github.com/rust-lang/rust/issues/120040.
             let last_error = api::get_last_error();
             if last_error == WinError::FILE_NOT_FOUND {
-                return Ok(ReadDir {
-                    handle: FindNextFileHandle(find_handle),
-                    root: Arc::new(root),
-                    first: None,
-                });
+                return Ok(ReadDir { handle: None, root: Arc::new(root), first: None });
             }
 
             // Just return the error constructed from the raw OS error if the above is not the case.
diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs
index 9a1eaf42fd9..90f1157d7c4 100644
--- a/library/std/src/sys/pal/windows/process/tests.rs
+++ b/library/std/src/sys/pal/windows/process/tests.rs
@@ -33,7 +33,7 @@ fn test_thread_handle() {
     assert!(p.is_ok());
     let mut p = p.unwrap();
 
-    extern "system" {
+    unsafe extern "system" {
         fn ResumeThread(_: BorrowedHandle<'_>) -> u32;
     }
     unsafe {
diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs
index b0ab01a6383..307623c0425 100644
--- a/library/std/src/sys/pal/xous/os.rs
+++ b/library/std/src/sys/pal/xous/os.rs
@@ -37,7 +37,7 @@ mod eh_unwinding {
 #[cfg(not(test))]
 mod c_compat {
     use crate::os::xous::ffi::exit;
-    extern "C" {
+    unsafe extern "C" {
         fn main() -> u32;
     }
 
diff --git a/library/std/src/sys/pal/zkvm/abi.rs b/library/std/src/sys/pal/zkvm/abi.rs
index 53332d90e02..d000574f684 100644
--- a/library/std/src/sys/pal/zkvm/abi.rs
+++ b/library/std/src/sys/pal/zkvm/abi.rs
@@ -18,7 +18,7 @@ pub mod fileno {
     pub const JOURNAL: u32 = 3;
 }
 
-extern "C" {
+unsafe extern "C" {
     // Wrappers around syscalls provided by risc0-zkvm-platform:
     pub fn sys_halt();
     pub fn sys_output(output_id: u32, output_value: u32);
diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs
index 88a25caeff0..cd2c7899f4b 100644
--- a/library/std/src/sys/personality/gcc.rs
+++ b/library/std/src/sys/personality/gcc.rs
@@ -194,7 +194,7 @@ cfg_if::cfg_if! {
                     }
                 }
                 // defined in libgcc
-                extern "C" {
+                unsafe extern "C" {
                     fn __gnu_unwind_frame(
                         exception_object: *mut uw::_Unwind_Exception,
                         context: *mut uw::_Unwind_Context,
diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs
index 32467e9ebaa..e1957bceb90 100644
--- a/library/std/src/sys/random/arc4random.rs
+++ b/library/std/src/sys/random/arc4random.rs
@@ -25,7 +25,7 @@ use libc::arc4random_buf;
     target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
 ))]
 #[cfg_attr(target_os = "haiku", link(name = "bsd"))]
-extern "C" {
+unsafe extern "C" {
     fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t);
 }
 
diff --git a/library/std/src/sys/random/espidf.rs b/library/std/src/sys/random/espidf.rs
index fd52cb5559c..6f48f7f1f29 100644
--- a/library/std/src/sys/random/espidf.rs
+++ b/library/std/src/sys/random/espidf.rs
@@ -1,6 +1,6 @@
 use crate::ffi::c_void;
 
-extern "C" {
+unsafe extern "C" {
     fn esp_fill_random(buf: *mut c_void, len: usize);
 }
 
diff --git a/library/std/src/sys/random/fuchsia.rs b/library/std/src/sys/random/fuchsia.rs
index 77d72b3c5b7..269e0d9aeeb 100644
--- a/library/std/src/sys/random/fuchsia.rs
+++ b/library/std/src/sys/random/fuchsia.rs
@@ -4,7 +4,7 @@
 //! <https://fuchsia.dev/reference/syscalls/cprng_draw>.
 
 #[link(name = "zircon")]
-extern "C" {
+unsafe extern "C" {
     fn zx_cprng_draw(buffer: *mut u8, len: usize);
 }
 
diff --git a/library/std/src/sys/random/teeos.rs b/library/std/src/sys/random/teeos.rs
index fd6b24e19e9..6ca59cc12c9 100644
--- a/library/std/src/sys/random/teeos.rs
+++ b/library/std/src/sys/random/teeos.rs
@@ -1,4 +1,4 @@
-extern "C" {
+unsafe extern "C" {
     fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t);
 }
 
diff --git a/library/std/src/sys/sync/thread_parking/darwin.rs b/library/std/src/sys/sync/thread_parking/darwin.rs
index 0553c5e19a9..a0d24a91e7c 100644
--- a/library/std/src/sys/sync/thread_parking/darwin.rs
+++ b/library/std/src/sys/sync/thread_parking/darwin.rs
@@ -24,7 +24,7 @@ const DISPATCH_TIME_NOW: dispatch_time_t = 0;
 const DISPATCH_TIME_FOREVER: dispatch_time_t = !0;
 
 // Contained in libSystem.dylib, which is linked by default.
-extern "C" {
+unsafe extern "C" {
     fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t;
     fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t;
     fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize;
diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs
index f473dc4d79d..817941229ee 100644
--- a/library/std/src/sys/thread_local/destructors/linux_like.rs
+++ b/library/std/src/sys/thread_local/destructors/linux_like.rs
@@ -27,7 +27,7 @@ pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
     #[allow(non_camel_case_types)]
     pub struct c_int(#[allow(dead_code)] pub core::ffi::c_int);
 
-    extern "C" {
+    unsafe extern "C" {
         #[linkage = "extern_weak"]
         static __dso_handle: *mut u8;
         #[linkage = "extern_weak"]
diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs
index fa25b116622..edcedf21e9e 100644
--- a/library/std/src/sys/thread_local/guard/apple.rs
+++ b/library/std/src/sys/thread_local/guard/apple.rs
@@ -10,7 +10,7 @@ pub fn enable() {
     #[thread_local]
     static REGISTERED: Cell<bool> = Cell::new(false);
 
-    extern "C" {
+    unsafe extern "C" {
         fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
     }
 
diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs
index 1752b0e1208..b15a0d7c0bd 100644
--- a/library/std/src/sys/thread_local/guard/windows.rs
+++ b/library/std/src/sys/thread_local/guard/windows.rs
@@ -74,7 +74,7 @@ pub fn enable() {
     unsafe { ptr::from_ref(&CALLBACK).read_volatile() };
 }
 
-#[link_section = ".CRT$XLB"]
+#[unsafe(link_section = ".CRT$XLB")]
 #[cfg_attr(miri, used)] // Miri only considers explicitly `#[used]` statics for `lookup_link_section`
 pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = tls_callback;
 
diff --git a/library/std/src/sys/thread_local/key/unix.rs b/library/std/src/sys/thread_local/key/unix.rs
index b4b58b34706..93bd0d1f668 100644
--- a/library/std/src/sys/thread_local/key/unix.rs
+++ b/library/std/src/sys/thread_local/key/unix.rs
@@ -8,7 +8,7 @@ mod libc {
     #[allow(non_camel_case_types)]
     pub type pthread_key_t = ffi::c_uint;
 
-    extern "C" {
+    unsafe extern "C" {
         pub fn pthread_key_create(
             key: *mut pthread_key_t,
             destructor: unsafe extern "C" fn(*mut ffi::c_void),
diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs
index 2ab4bba7d8e..6c5e6447d19 100644
--- a/library/std/src/sys/thread_local/key/xous.rs
+++ b/library/std/src/sys/thread_local/key/xous.rs
@@ -59,7 +59,7 @@ static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1);
 static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
 
 #[cfg(test)]
-extern "Rust" {
+unsafe extern "Rust" {
     #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"]
     static TLS_KEY_INDEX: AtomicUsize;
 
diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs
index 42011a9b3da..91fe0200f6c 100644
--- a/library/std/tests/switch-stdout.rs
+++ b/library/std/tests/switch-stdout.rs
@@ -14,7 +14,7 @@ use std::os::windows::io::OwnedHandle;
 fn switch_stdout_to(file: OwnedFd) -> OwnedFd {
     use std::os::unix::prelude::*;
 
-    extern "C" {
+    unsafe extern "C" {
         fn dup(old: i32) -> i32;
         fn dup2(old: i32, new: i32) -> i32;
     }
@@ -32,7 +32,7 @@ fn switch_stdout_to(file: OwnedFd) -> OwnedFd {
 fn switch_stdout_to(file: OwnedHandle) -> OwnedHandle {
     use std::os::windows::prelude::*;
 
-    extern "system" {
+    unsafe extern "system" {
         fn GetStdHandle(nStdHandle: u32) -> *mut u8;
         fn SetStdHandle(nStdHandle: u32, handle: *mut u8) -> i32;
     }
diff --git a/library/std/tests/win_delete_self.rs b/library/std/tests/win_delete_self.rs
index 1c3ce4d710c..ce505de69a2 100644
--- a/library/std/tests/win_delete_self.rs
+++ b/library/std/tests/win_delete_self.rs
@@ -2,6 +2,7 @@
 
 /// Attempting to delete a running binary should return an error on Windows.
 #[test]
+#[cfg_attr(miri, ignore)] // `remove_file` does not work in Miri on Windows
 fn win_delete_self() {
     let path = std::env::current_exe().unwrap();
     assert!(std::fs::remove_file(path).is_err());
diff --git a/library/test/src/console.rs b/library/test/src/console.rs
index 024ef48fc51..8f29f1dada5 100644
--- a/library/test/src/console.rs
+++ b/library/test/src/console.rs
@@ -314,9 +314,10 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
     let mut st = ConsoleTestState::new(opts)?;
 
     // Prevent the usage of `Instant` in some cases:
-    // - It's currently not supported for wasm targets.
+    // - It's currently not supported for wasm targets without Emscripten nor WASI.
+    // - It's currently not supported for zkvm targets.
     let is_instant_unsupported =
-        (cfg!(target_family = "wasm") && !cfg!(target_os = "wasi")) || cfg!(target_os = "zkvm");
+        (cfg!(target_family = "wasm") && cfg!(target_os = "unknown")) || cfg!(target_os = "zkvm");
 
     let start_time = (!is_instant_unsupported).then(Instant::now);
     run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?;
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 54f7e4ae79f..e523d302866 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -184,12 +184,16 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
     // If we're being run in SpawnedSecondary mode, run the test here. run_test
     // will then exit the process.
     if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) {
-        env::remove_var(SECONDARY_TEST_INVOKER_VAR);
+        unsafe {
+            env::remove_var(SECONDARY_TEST_INVOKER_VAR);
+        }
 
         // Convert benchmarks to tests if we're not benchmarking.
         let mut tests = tests.iter().map(make_owned_test).collect::<Vec<_>>();
         if env::var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR).is_ok() {
-            env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR);
+            unsafe {
+                env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR);
+            }
         } else {
             tests = convert_benchmarks_to_tests(tests);
         };
diff --git a/library/test/src/term/win.rs b/library/test/src/term/win.rs
index c77e6aac478..62e5c43ea27 100644
--- a/library/test/src/term/win.rs
+++ b/library/test/src/term/win.rs
@@ -52,7 +52,7 @@ struct CONSOLE_SCREEN_BUFFER_INFO {
 
 #[allow(non_snake_case)]
 #[link(name = "kernel32")]
-extern "system" {
+unsafe extern "system" {
     fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL;
     fn GetStdHandle(which: DWORD) -> HANDLE;
     fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL;
diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs
index e4ba2bc1ed8..2650b273a4d 100644
--- a/library/unwind/src/lib.rs
+++ b/library/unwind/src/lib.rs
@@ -56,15 +56,15 @@ cfg_if::cfg_if! {
         compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time");
     } else if #[cfg(feature = "llvm-libunwind")] {
         #[link(name = "unwind", kind = "static", modifiers = "-bundle")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else if #[cfg(feature = "system-llvm-libunwind")] {
         #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
         #[link(name = "unwind", cfg(not(target_feature = "crt-static")))]
-        extern "C" {}
+        unsafe extern "C" {}
     } else {
         #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
         #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))]
-        extern "C" {}
+        unsafe extern "C" {}
     }
 }
 
@@ -76,11 +76,11 @@ cfg_if::cfg_if! {
         compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time");
     } else if #[cfg(feature = "llvm-libunwind")] {
         #[link(name = "unwind", kind = "static", modifiers = "-bundle")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else {
         #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
         #[link(name = "unwind", cfg(not(target_feature = "crt-static")))]
-        extern "C" {}
+        unsafe extern "C" {}
     }
 }
 
@@ -91,14 +91,14 @@ cfg_if::cfg_if! {
     } else {
         #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
         #[link(name = "unwind", cfg(not(target_feature = "crt-static")))]
-        extern "C" {}
+        unsafe extern "C" {}
     }
 }
 // Android's unwinding library depends on dl_iterate_phdr in `libdl`.
 #[cfg(target_os = "android")]
 #[link(name = "dl", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
 #[link(name = "dl", cfg(not(target_feature = "crt-static")))]
-extern "C" {}
+unsafe extern "C" {}
 
 // When building with crt-static, we get `gcc_eh` from the `libc` crate, since
 // glibc needs it, and needs it listed later on the linker command line. We
@@ -110,7 +110,7 @@ extern "C" {}
     not(feature = "system-llvm-libunwind")
 ))]
 #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(all(
     target_os = "linux",
@@ -119,67 +119,67 @@ extern "C" {}
     feature = "system-llvm-libunwind"
 ))]
 #[link(name = "unwind", cfg(not(target_feature = "crt-static")))]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(target_os = "redox")]
 #[link(name = "gcc_eh", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
 #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
 #[link(name = "unwind", kind = "static", modifiers = "-bundle")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(target_os = "netbsd")]
 #[link(name = "gcc_s")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(target_os = "freebsd")]
 #[link(name = "gcc", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
 #[link(name = "gcc_eh", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
 #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(all(target_os = "openbsd", target_arch = "sparc64"))]
 #[link(name = "gcc")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(all(target_os = "openbsd", not(target_arch = "sparc64")))]
 #[link(name = "c++abi")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
 #[link(name = "gcc_s")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(target_os = "dragonfly")]
 #[link(name = "gcc_pic")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(target_os = "haiku")]
 #[link(name = "gcc_s")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(target_os = "aix")]
 #[link(name = "unwind")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(target_os = "nto")]
 cfg_if::cfg_if! {
     if #[cfg(target_env = "nto70")] {
         #[link(name = "gcc")]
-        extern "C" {}
+        unsafe extern "C" {}
     } else {
         #[link(name = "gcc_s")]
-        extern "C" {}
+        unsafe extern "C" {}
     }
 }
 
 #[cfg(target_os = "hurd")]
 #[link(name = "gcc_s")]
-extern "C" {}
+unsafe extern "C" {}
 
 #[cfg(all(target_os = "windows", target_env = "gnu", target_abi = "llvm"))]
 #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
 #[link(name = "unwind", cfg(not(target_feature = "crt-static")))]
-extern "C" {}
+unsafe extern "C" {}
diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs
index 1fa9e480166..62165f8a200 100644
--- a/library/unwind/src/libunwind.rs
+++ b/library/unwind/src/libunwind.rs
@@ -108,10 +108,10 @@ pub type _Unwind_Exception_Cleanup_Fn =
     ),
     link(name = "unwind", kind = "static", modifiers = "-bundle")
 )]
-extern "C-unwind" {
+unsafe extern "C-unwind" {
     pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
 }
-extern "C" {
+unsafe extern "C" {
     pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
     pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
     pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
@@ -140,7 +140,7 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a
         all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")),
         link(name = "unwind", kind = "static", modifiers = "-bundle")
     )]
-    extern "C" {
+    unsafe extern "C" {
         pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word;
         pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word);
         pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> _Unwind_Word;
@@ -198,7 +198,7 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a
         all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")),
         link(name = "unwind", kind = "static", modifiers = "-bundle")
     )]
-    extern "C" {
+    unsafe extern "C" {
         fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
                            regclass: _Unwind_VRS_RegClass,
                            regno: _Unwind_Word,
@@ -261,7 +261,7 @@ cfg_if::cfg_if! {
 if #[cfg(all(target_vendor = "apple", not(target_os = "watchos"), target_arch = "arm"))] {
     // 32-bit ARM Apple (except for watchOS armv7k specifically) uses SjLj and
     // does not provide _Unwind_Backtrace()
-    extern "C-unwind" {
+    unsafe extern "C-unwind" {
         pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
     }
 
@@ -271,14 +271,14 @@ if #[cfg(all(target_vendor = "apple", not(target_os = "watchos"), target_arch =
         all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")),
         link(name = "unwind", kind = "static", modifiers = "-bundle")
     )]
-    extern "C-unwind" {
+    unsafe extern "C-unwind" {
         pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
     }
     #[cfg_attr(
         all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")),
         link(name = "unwind", kind = "static", modifiers = "-bundle")
     )]
-    extern "C" {
+    unsafe extern "C" {
         pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
                                  trace_argument: *mut c_void)
                                  -> _Unwind_Reason_Code;
@@ -302,7 +302,7 @@ if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), targ
                                               context: *mut _Unwind_Context)
                                               -> _Unwind_Reason_Code;
 
-    extern "C" {
+    unsafe extern "C" {
         pub fn _GCC_specific_handler(exceptionRecord: *mut EXCEPTION_RECORD,
                                 establisherFrame: LPVOID,
                                 contextRecord: *mut CONTEXT,
diff --git a/library/windows_targets/src/lib.rs b/library/windows_targets/src/lib.rs
index 395cd6a4bab..e89bde8b1ab 100644
--- a/library/windows_targets/src/lib.rs
+++ b/library/windows_targets/src/lib.rs
@@ -39,4 +39,4 @@ pub macro link {
 #[link(name = "userenv")]
 #[link(name = "ws2_32")]
 #[link(name = "dbghelp")] // required for backtrace-rs symbolization
-extern "C" {}
+unsafe extern "C" {}
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 825e5452f0e..b0181410be1 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1648,16 +1648,17 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         // bootstrap compiler.
         // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the
         // running compiler in stage 2 when plugins run.
-        let stage_id = if suite == "ui-fulldeps" && compiler.stage == 1 {
-            // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead finding
-            // an incorrect compiler path on cross-targets, as the stage 0 beta compiler is always equal
-            // to `build.build` in the configuration.
+        let (stage, stage_id) = if suite == "ui-fulldeps" && compiler.stage == 1 {
+            // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead
+            // finding an incorrect compiler path on cross-targets, as the stage 0 beta compiler is
+            // always equal to `build.build` in the configuration.
             let build = builder.build.build;
-
             compiler = builder.compiler(compiler.stage - 1, build);
-            format!("stage{}-{}", compiler.stage + 1, build)
+            let test_stage = compiler.stage + 1;
+            (test_stage, format!("stage{}-{}", test_stage, build))
         } else {
-            format!("stage{}-{}", compiler.stage, target)
+            let stage = compiler.stage;
+            (stage, format!("stage{}-{}", stage, target))
         };
 
         if suite.ends_with("fulldeps") {
@@ -1699,6 +1700,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         // compiletest currently has... a lot of arguments, so let's just pass all
         // of them!
 
+        cmd.arg("--stage").arg(stage.to_string());
+        cmd.arg("--stage-id").arg(stage_id);
+
         cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(compiler));
         cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(compiler, target));
         cmd.arg("--rustc-path").arg(builder.rustc(compiler));
@@ -1767,8 +1771,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         } else {
             builder.sysroot(compiler).to_path_buf()
         };
+
         cmd.arg("--sysroot-base").arg(sysroot);
-        cmd.arg("--stage-id").arg(stage_id);
+
         cmd.arg("--suite").arg(suite);
         cmd.arg("--mode").arg(mode);
         cmd.arg("--target").arg(target.rustc_target_arg());
diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh
index 8850e168145..32d55806b56 100755
--- a/src/ci/scripts/free-disk-space.sh
+++ b/src/ci/scripts/free-disk-space.sh
@@ -110,6 +110,9 @@ execAndMeasureSpaceChange() {
 # Remove large packages
 # REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh
 cleanPackages() {
+    # Stop services to avoid issues when removing their packages.
+    sudo systemctl stop mysql
+
     local packages=(
         '^aspnetcore-.*'
         '^dotnet-.*'
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 00821fc9f9d..cde4f7a665c 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -224,7 +224,9 @@ pub struct Config {
     /// The directory containing the compiler sysroot
     pub sysroot_base: PathBuf,
 
-    /// The name of the stage being built (stage1, etc)
+    /// The number of the stage under test.
+    pub stage: u32,
+    /// The id of the stage under test (stage1-xxx, etc).
     pub stage_id: String,
 
     /// The test mode, e.g. ui or debuginfo.
diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs
index 6e5ced17c20..cfe51b5655f 100644
--- a/src/tools/compiletest/src/header/cfg.rs
+++ b/src/tools/compiletest/src/header/cfg.rs
@@ -192,7 +192,7 @@ fn parse_cfg_name_directive<'a>(
         message: "on big-endian targets",
     }
     condition! {
-        name: config.stage_id.split('-').next().unwrap(),
+        name: format!("stage{}", config.stage).as_str(),
         allowed_names: &["stage0", "stage1", "stage2"],
         message: "when the bootstrapping stage is {name}",
     }
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 023658a3dd4..522d340b678 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -72,6 +72,7 @@ struct ConfigBuilder {
     channel: Option<String>,
     host: Option<String>,
     target: Option<String>,
+    stage: Option<u32>,
     stage_id: Option<String>,
     llvm_version: Option<String>,
     git_hash: bool,
@@ -102,6 +103,11 @@ impl ConfigBuilder {
         self
     }
 
+    fn stage(&mut self, n: u32) -> &mut Self {
+        self.stage = Some(n);
+        self
+    }
+
     fn stage_id(&mut self, s: &str) -> &mut Self {
         self.stage_id = Some(s.to_owned());
         self
@@ -156,6 +162,8 @@ impl ConfigBuilder {
             "--cxxflags=",
             "--llvm-components=",
             "--android-cross-path=",
+            "--stage",
+            &self.stage.unwrap_or(2).to_string(),
             "--stage-id",
             self.stage_id.as_deref().unwrap_or("stage2-x86_64-unknown-linux-gnu"),
             "--channel",
@@ -388,7 +396,7 @@ fn std_debug_assertions() {
 
 #[test]
 fn stage() {
-    let config: Config = cfg().stage_id("stage1-x86_64-unknown-linux-gnu").build();
+    let config: Config = cfg().stage(1).stage_id("stage1-x86_64-unknown-linux-gnu").build();
 
     assert!(check_ignore(&config, "//@ ignore-stage1"));
     assert!(!check_ignore(&config, "//@ ignore-stage2"));
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 27a046ba5bc..d0a83cab9cd 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -64,6 +64,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         .reqopt("", "src-base", "directory to scan for test files", "PATH")
         .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
         .reqopt("", "sysroot-base", "directory containing the compiler sysroot", "PATH")
+        .reqopt("", "stage", "stage number under test", "N")
         .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
         .reqopt(
             "",
@@ -294,6 +295,11 @@ pub fn parse_config(args: Vec<String>) -> Config {
         panic!("`--nocapture` is deprecated; please use `--no-capture`");
     }
 
+    let stage = match matches.opt_str("stage") {
+        Some(stage) => stage.parse::<u32>().expect("expected `--stage` to be an unsigned integer"),
+        None => panic!("`--stage` is required"),
+    };
+
     Config {
         bless: matches.opt_present("bless"),
         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
@@ -311,7 +317,10 @@ pub fn parse_config(args: Vec<String>) -> Config {
         src_base,
         build_base: opt_path(matches, "build-base"),
         sysroot_base: opt_path(matches, "sysroot-base"),
+
+        stage,
         stage_id: matches.opt_str("stage-id").unwrap(),
+
         mode,
         suite: matches.opt_str("suite").unwrap(),
         debugger: matches.opt_str("debugger").map(|debugger| {
@@ -415,6 +424,7 @@ pub fn log_config(config: &Config) {
     logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
     logv(c, format!("src_base: {:?}", config.src_base.display()));
     logv(c, format!("build_base: {:?}", config.build_base.display()));
+    logv(c, format!("stage: {}", config.stage));
     logv(c, format!("stage_id: {}", config.stage_id));
     logv(c, format!("mode: {}", config.mode));
     logv(c, format!("run_ignored: {}", config.run_ignored));
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 7ef16e4a966..16c46fc1339 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -239,30 +239,6 @@ impl TestCx<'_> {
             }
         }
 
-        // `self.config.stage_id` looks like `stage1-<target_triple>`, but we only want
-        // the `stage1` part as that is what the output directories of bootstrap are prefixed with.
-        // Note that this *assumes* build layout from bootstrap is produced as:
-        //
-        // ```
-        // build/<target_triple>/          // <- this is `build_root`
-        // ├── stage0
-        // ├── stage0-bootstrap-tools
-        // ├── stage0-codegen
-        // ├── stage0-rustc
-        // ├── stage0-std
-        // ├── stage0-sysroot
-        // ├── stage0-tools
-        // ├── stage0-tools-bin
-        // ├── stage1
-        // ├── stage1-std
-        // ├── stage1-tools
-        // ├── stage1-tools-bin
-        // └── test
-        // ```
-        // FIXME(jieyouxu): improve the communication between bootstrap and compiletest here so
-        // we don't have to hack out a `stageN`.
-        let stage = self.config.stage_id.split('-').next().unwrap();
-
         // In order to link in the support library as a rlib when compiling recipes, we need three
         // paths:
         // 1. Path of the built support library rlib itself.
@@ -284,10 +260,12 @@ impl TestCx<'_> {
         // support lib and its deps are organized, can't we copy them to the tools-bin dir as
         // well?), but this seems to work for now.
 
-        let stage_tools_bin = build_root.join(format!("{stage}-tools-bin"));
+        let stage_number = self.config.stage;
+
+        let stage_tools_bin = build_root.join(format!("stage{stage_number}-tools-bin"));
         let support_lib_path = stage_tools_bin.join("librun_make_support.rlib");
 
-        let stage_tools = build_root.join(format!("{stage}-tools"));
+        let stage_tools = build_root.join(format!("stage{stage_number}-tools"));
         let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps");
         let support_lib_deps_deps = stage_tools.join("release").join("deps");
 
@@ -368,7 +346,7 @@ impl TestCx<'_> {
         // provided through env vars.
 
         // Compute stage-specific standard library paths.
-        let stage_std_path = build_root.join(&stage).join("lib");
+        let stage_std_path = build_root.join(format!("stage{stage_number}")).join("lib");
 
         // Compute dynamic library search paths for recipes.
         let recipe_dylib_search_paths = {
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index faa0db27b2b..5478ff4a6c6 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -285,7 +285,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "expect-test",
     "fallible-iterator", // dependency of `thorin`
     "fastrand",
-    "field-offset",
     "flate2",
     "fluent-bundle",
     "fluent-langneg",
@@ -327,7 +326,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "measureme",
     "memchr",
     "memmap2",
-    "memoffset",
     "miniz_oxide",
     "nix",
     "nu-ansi-term",
@@ -367,14 +365,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "rustc-rayon-core",
     "rustc-stable-hash",
     "rustc_apfloat",
-    "rustc_version",
     "rustix",
     "ruzstd", // via object in thorin-dwp
     "ryu",
     "scoped-tls",
     "scopeguard",
     "self_cell",
-    "semver",
     "serde",
     "serde_derive",
     "serde_json",
diff --git a/tests/coverage/assert_not.cov-map b/tests/coverage/assert_not.cov-map
index 397eaa17caf..35568a98af4 100644
--- a/tests/coverage/assert_not.cov-map
+++ b/tests/coverage/assert_not.cov-map
@@ -1,15 +1,13 @@
 Function name: assert_not::main
-Raw bytes (31): 0x[01, 01, 01, 0d, 00, 05, 01, 06, 01, 01, 12, 05, 02, 05, 00, 14, 09, 01, 05, 00, 14, 0d, 01, 05, 00, 16, 02, 01, 01, 00, 02]
+Raw bytes (29): 0x[01, 01, 00, 05, 01, 06, 01, 01, 12, 05, 02, 05, 00, 14, 09, 01, 05, 00, 14, 0d, 01, 05, 00, 16, 0d, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(3), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 5
 - Code(Counter(0)) at (prev + 6, 1) to (start + 1, 18)
 - Code(Counter(1)) at (prev + 2, 5) to (start + 0, 20)
 - Code(Counter(2)) at (prev + 1, 5) to (start + 0, 20)
 - Code(Counter(3)) at (prev + 1, 5) to (start + 0, 22)
-- Code(Expression(0, Sub)) at (prev + 1, 1) to (start + 0, 2)
-    = (c3 - Zero)
+- Code(Counter(3)) at (prev + 1, 1) to (start + 0, 2)
 Highest counter ID seen: c3
 
diff --git a/tests/coverage/bad_counter_ids.cov-map b/tests/coverage/bad_counter_ids.cov-map
index 2b5399f33bb..ba306278449 100644
--- a/tests/coverage/bad_counter_ids.cov-map
+++ b/tests/coverage/bad_counter_ids.cov-map
@@ -9,15 +9,13 @@ Number of file 0 mappings: 2
 Highest counter ID seen: c0
 
 Function name: bad_counter_ids::eq_bad_message
-Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 29, 01, 02, 0f, 02, 02, 20, 00, 2b, 00, 01, 01, 00, 02]
+Raw bytes (19): 0x[01, 01, 00, 03, 01, 29, 01, 02, 0f, 01, 02, 20, 00, 2b, 00, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 3
 - Code(Counter(0)) at (prev + 41, 1) to (start + 2, 15)
-- Code(Expression(0, Sub)) at (prev + 2, 32) to (start + 0, 43)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 2, 32) to (start + 0, 43)
 - Code(Zero) at (prev + 1, 1) to (start + 0, 2)
 Highest counter ID seen: c0
 
@@ -64,27 +62,23 @@ Number of file 0 mappings: 3
 Highest counter ID seen: c1
 
 Function name: bad_counter_ids::ne_good
-Raw bytes (16): 0x[01, 01, 01, 01, 00, 02, 01, 1a, 01, 02, 1f, 02, 03, 01, 00, 02]
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 1a, 01, 02, 1f, 01, 03, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 2
 - Code(Counter(0)) at (prev + 26, 1) to (start + 2, 31)
-- Code(Expression(0, Sub)) at (prev + 3, 1) to (start + 0, 2)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2)
 Highest counter ID seen: c0
 
 Function name: bad_counter_ids::ne_good_message
-Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 1f, 01, 02, 0f, 00, 02, 20, 00, 2b, 02, 01, 01, 00, 02]
+Raw bytes (19): 0x[01, 01, 00, 03, 01, 1f, 01, 02, 0f, 00, 02, 20, 00, 2b, 01, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 3
 - Code(Counter(0)) at (prev + 31, 1) to (start + 2, 15)
 - Code(Zero) at (prev + 2, 32) to (start + 0, 43)
-- Code(Expression(0, Sub)) at (prev + 1, 1) to (start + 0, 2)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
 Highest counter ID seen: c0
 
diff --git a/tests/coverage/fn_sig_into_try.cov-map b/tests/coverage/fn_sig_into_try.cov-map
index 374811dba9e..6d6034928c9 100644
--- a/tests/coverage/fn_sig_into_try.cov-map
+++ b/tests/coverage/fn_sig_into_try.cov-map
@@ -8,44 +8,38 @@ Number of file 0 mappings: 1
 Highest counter ID seen: c0
 
 Function name: fn_sig_into_try::b
-Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 11, 01, 03, 0f, 00, 03, 0f, 00, 10, 02, 01, 05, 00, 0c, 01, 01, 01, 00, 02]
+Raw bytes (24): 0x[01, 01, 00, 04, 01, 11, 01, 03, 0f, 00, 03, 0f, 00, 10, 01, 01, 05, 00, 0c, 01, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 4
 - Code(Counter(0)) at (prev + 17, 1) to (start + 3, 15)
 - Code(Zero) at (prev + 3, 15) to (start + 0, 16)
-- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 12)
 - Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
 Highest counter ID seen: c0
 
 Function name: fn_sig_into_try::c
-Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 18, 01, 03, 17, 00, 03, 17, 00, 18, 02, 01, 05, 00, 0c, 01, 01, 01, 00, 02]
+Raw bytes (24): 0x[01, 01, 00, 04, 01, 18, 01, 03, 17, 00, 03, 17, 00, 18, 01, 01, 05, 00, 0c, 01, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 4
 - Code(Counter(0)) at (prev + 24, 1) to (start + 3, 23)
 - Code(Zero) at (prev + 3, 23) to (start + 0, 24)
-- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 12)
 - Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
 Highest counter ID seen: c0
 
 Function name: fn_sig_into_try::d
-Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 1f, 01, 04, 0f, 00, 04, 0f, 00, 10, 02, 01, 05, 00, 0c, 01, 01, 01, 00, 02]
+Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 01, 04, 0f, 00, 04, 0f, 00, 10, 01, 01, 05, 00, 0c, 01, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 4
 - Code(Counter(0)) at (prev + 31, 1) to (start + 4, 15)
 - Code(Zero) at (prev + 4, 15) to (start + 0, 16)
-- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 12)
 - Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
 Highest counter ID seen: c0
 
diff --git a/tests/coverage/inline-dead.cov-map b/tests/coverage/inline-dead.cov-map
index 3e2ca2bc992..49cdc514fed 100644
--- a/tests/coverage/inline-dead.cov-map
+++ b/tests/coverage/inline-dead.cov-map
@@ -32,16 +32,13 @@ Number of file 0 mappings: 2
 Highest counter ID seen: c0
 
 Function name: inline_dead::main::{closure#0}
-Raw bytes (23): 0x[01, 01, 02, 07, 00, 01, 00, 03, 01, 07, 17, 01, 16, 00, 01, 17, 00, 18, 02, 01, 05, 00, 06]
+Raw bytes (19): 0x[01, 01, 00, 03, 01, 07, 17, 01, 16, 00, 01, 17, 00, 18, 01, 01, 05, 00, 06]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 2
-- expression 0 operands: lhs = Expression(1, Add), rhs = Zero
-- expression 1 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 3
 - Code(Counter(0)) at (prev + 7, 23) to (start + 1, 22)
 - Code(Zero) at (prev + 1, 23) to (start + 0, 24)
-- Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 6)
-    = ((c0 + Zero) - Zero)
+- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6)
 Highest counter ID seen: c0
 
diff --git a/tests/coverage/issue-84561.cov-map b/tests/coverage/issue-84561.cov-map
index efb9d43bf5b..c8f75cddcb5 100644
--- a/tests/coverage/issue-84561.cov-map
+++ b/tests/coverage/issue-84561.cov-map
@@ -59,10 +59,10 @@ Number of file 0 mappings: 1
 Highest counter ID seen: c0
 
 Function name: issue_84561::test3
-Raw bytes (317): 0x[01, 01, 1c, 1d, 21, 25, 29, 21, 25, 2d, 31, 21, 17, 25, 2d, 41, 45, 49, 4d, 51, 55, 33, 51, 49, 4d, 33, 37, 49, 4d, 51, 59, 55, 59, 55, 59, 47, 5d, 55, 59, 61, 65, 71, 75, 69, 6d, 69, 6d, 69, 5f, 6d, 00, 67, 79, 71, 75, 79, 7d, 7d, 81, 01, 33, 01, 08, 01, 03, 1c, 05, 04, 09, 01, 1c, 09, 02, 05, 04, 1f, 0d, 05, 05, 00, 1f, 11, 01, 05, 00, 1f, 15, 01, 09, 01, 1c, 19, 02, 05, 00, 1f, 1d, 01, 05, 00, 0f, 02, 00, 20, 00, 30, 21, 01, 05, 03, 0f, 25, 03, 20, 00, 30, 29, 00, 33, 00, 41, 06, 00, 4b, 00, 5a, 0a, 01, 05, 00, 0f, 2d, 05, 09, 03, 10, 31, 05, 0d, 00, 1b, 0e, 02, 0d, 00, 1c, 12, 04, 09, 05, 06, 35, 06, 05, 03, 06, 39, 04, 05, 03, 06, 3d, 04, 09, 04, 06, 41, 05, 08, 00, 0f, 45, 01, 09, 03, 0a, 1a, 05, 09, 03, 0a, 33, 05, 08, 00, 0f, 51, 01, 09, 00, 13, 22, 03, 0d, 00, 1d, 26, 03, 09, 00, 13, 2e, 03, 0d, 00, 1d, 47, 03, 05, 00, 0f, 47, 01, 0c, 00, 13, 5d, 01, 0d, 00, 13, 42, 02, 0d, 00, 13, 61, 04, 05, 02, 13, 65, 03, 0d, 00, 13, 4a, 02, 0d, 00, 13, 67, 03, 05, 00, 0f, 69, 01, 0c, 00, 13, 6d, 01, 0d, 03, 0e, 71, 04, 0d, 00, 13, 56, 02, 0d, 00, 17, 56, 01, 14, 00, 1b, 00, 01, 15, 00, 1b, 5a, 02, 15, 00, 1b, 75, 04, 0d, 00, 13, 62, 03, 09, 00, 19, 79, 02, 05, 00, 0f, 6a, 03, 09, 00, 22, 7d, 02, 05, 00, 0f, 6e, 03, 09, 00, 2c, 81, 01, 02, 01, 00, 02]
+Raw bytes (315): 0x[01, 01, 1b, 1d, 21, 25, 29, 21, 25, 2d, 31, 21, 17, 25, 2d, 41, 45, 49, 4d, 51, 55, 33, 51, 49, 4d, 33, 37, 49, 4d, 51, 59, 55, 59, 55, 59, 47, 5d, 55, 59, 61, 65, 71, 75, 69, 6d, 69, 6d, 69, 6d, 63, 79, 71, 75, 79, 7d, 7d, 81, 01, 33, 01, 08, 01, 03, 1c, 05, 04, 09, 01, 1c, 09, 02, 05, 04, 1f, 0d, 05, 05, 00, 1f, 11, 01, 05, 00, 1f, 15, 01, 09, 01, 1c, 19, 02, 05, 00, 1f, 1d, 01, 05, 00, 0f, 02, 00, 20, 00, 30, 21, 01, 05, 03, 0f, 25, 03, 20, 00, 30, 29, 00, 33, 00, 41, 06, 00, 4b, 00, 5a, 0a, 01, 05, 00, 0f, 2d, 05, 09, 03, 10, 31, 05, 0d, 00, 1b, 0e, 02, 0d, 00, 1c, 12, 04, 09, 05, 06, 35, 06, 05, 03, 06, 39, 04, 05, 03, 06, 3d, 04, 09, 04, 06, 41, 05, 08, 00, 0f, 45, 01, 09, 03, 0a, 1a, 05, 09, 03, 0a, 33, 05, 08, 00, 0f, 51, 01, 09, 00, 13, 22, 03, 0d, 00, 1d, 26, 03, 09, 00, 13, 2e, 03, 0d, 00, 1d, 47, 03, 05, 00, 0f, 47, 01, 0c, 00, 13, 5d, 01, 0d, 00, 13, 42, 02, 0d, 00, 13, 61, 04, 05, 02, 13, 65, 03, 0d, 00, 13, 4a, 02, 0d, 00, 13, 63, 03, 05, 00, 0f, 69, 01, 0c, 00, 13, 6d, 01, 0d, 03, 0e, 71, 04, 0d, 00, 13, 5a, 02, 0d, 00, 17, 5a, 01, 14, 00, 1b, 00, 01, 15, 00, 1b, 5a, 02, 15, 00, 1b, 75, 04, 0d, 00, 13, 5e, 03, 09, 00, 19, 79, 02, 05, 00, 0f, 66, 03, 09, 00, 22, 7d, 02, 05, 00, 0f, 6a, 03, 09, 00, 2c, 81, 01, 02, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 28
+Number of expressions: 27
 - expression 0 operands: lhs = Counter(7), rhs = Counter(8)
 - expression 1 operands: lhs = Counter(9), rhs = Counter(10)
 - expression 2 operands: lhs = Counter(8), rhs = Counter(9)
@@ -85,12 +85,11 @@ Number of expressions: 28
 - expression 19 operands: lhs = Counter(28), rhs = Counter(29)
 - expression 20 operands: lhs = Counter(26), rhs = Counter(27)
 - expression 21 operands: lhs = Counter(26), rhs = Counter(27)
-- expression 22 operands: lhs = Counter(26), rhs = Expression(23, Add)
-- expression 23 operands: lhs = Counter(27), rhs = Zero
-- expression 24 operands: lhs = Expression(25, Add), rhs = Counter(30)
-- expression 25 operands: lhs = Counter(28), rhs = Counter(29)
-- expression 26 operands: lhs = Counter(30), rhs = Counter(31)
-- expression 27 operands: lhs = Counter(31), rhs = Counter(32)
+- expression 22 operands: lhs = Counter(26), rhs = Counter(27)
+- expression 23 operands: lhs = Expression(24, Add), rhs = Counter(30)
+- expression 24 operands: lhs = Counter(28), rhs = Counter(29)
+- expression 25 operands: lhs = Counter(30), rhs = Counter(31)
+- expression 26 operands: lhs = Counter(31), rhs = Counter(32)
 Number of file 0 mappings: 51
 - Code(Counter(0)) at (prev + 8, 1) to (start + 3, 28)
 - Code(Counter(1)) at (prev + 4, 9) to (start + 1, 28)
@@ -142,26 +141,26 @@ Number of file 0 mappings: 51
 - Code(Counter(25)) at (prev + 3, 13) to (start + 0, 19)
 - Code(Expression(18, Sub)) at (prev + 2, 13) to (start + 0, 19)
     = (c24 - c25)
-- Code(Expression(25, Add)) at (prev + 3, 5) to (start + 0, 15)
+- Code(Expression(24, Add)) at (prev + 3, 5) to (start + 0, 15)
     = (c28 + c29)
 - Code(Counter(26)) at (prev + 1, 12) to (start + 0, 19)
 - Code(Counter(27)) at (prev + 1, 13) to (start + 3, 14)
 - Code(Counter(28)) at (prev + 4, 13) to (start + 0, 19)
-- Code(Expression(21, Sub)) at (prev + 2, 13) to (start + 0, 23)
+- Code(Expression(22, Sub)) at (prev + 2, 13) to (start + 0, 23)
     = (c26 - c27)
-- Code(Expression(21, Sub)) at (prev + 1, 20) to (start + 0, 27)
+- Code(Expression(22, Sub)) at (prev + 1, 20) to (start + 0, 27)
     = (c26 - c27)
 - Code(Zero) at (prev + 1, 21) to (start + 0, 27)
 - Code(Expression(22, Sub)) at (prev + 2, 21) to (start + 0, 27)
-    = (c26 - (c27 + Zero))
+    = (c26 - c27)
 - Code(Counter(29)) at (prev + 4, 13) to (start + 0, 19)
-- Code(Expression(24, Sub)) at (prev + 3, 9) to (start + 0, 25)
+- Code(Expression(23, Sub)) at (prev + 3, 9) to (start + 0, 25)
     = ((c28 + c29) - c30)
 - Code(Counter(30)) at (prev + 2, 5) to (start + 0, 15)
-- Code(Expression(26, Sub)) at (prev + 3, 9) to (start + 0, 34)
+- Code(Expression(25, Sub)) at (prev + 3, 9) to (start + 0, 34)
     = (c30 - c31)
 - Code(Counter(31)) at (prev + 2, 5) to (start + 0, 15)
-- Code(Expression(27, Sub)) at (prev + 3, 9) to (start + 0, 44)
+- Code(Expression(26, Sub)) at (prev + 3, 9) to (start + 0, 44)
     = (c31 - c32)
 - Code(Counter(32)) at (prev + 2, 1) to (start + 0, 2)
 Highest counter ID seen: c32
diff --git a/tests/coverage/loops_branches.cov-map b/tests/coverage/loops_branches.cov-map
index 912141b6d5f..640d5f15be0 100644
--- a/tests/coverage/loops_branches.cov-map
+++ b/tests/coverage/loops_branches.cov-map
@@ -1,85 +1,70 @@
 Function name: <loops_branches::DebugTest as core::fmt::Debug>::fmt
-Raw bytes (116): 0x[01, 01, 06, 05, 00, 1d, 00, 0f, 13, 01, 19, 11, 15, 15, 19, 14, 01, 09, 05, 01, 10, 05, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 02, 01, 0d, 00, 0e, 05, 01, 0d, 00, 1e, 11, 00, 1e, 00, 1f, 00, 01, 10, 01, 0a, 19, 03, 0d, 00, 0e, 15, 00, 12, 00, 17, 19, 01, 10, 00, 14, 1d, 01, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 06, 01, 11, 00, 12, 1d, 01, 11, 00, 22, 0a, 00, 22, 00, 23, 00, 01, 14, 01, 0e, 16, 03, 09, 00, 0f, 01, 01, 05, 00, 06]
+Raw bytes (112): 0x[01, 01, 04, 07, 0b, 01, 11, 09, 0d, 0d, 11, 14, 01, 09, 05, 01, 10, 05, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 05, 01, 0d, 00, 0e, 05, 01, 0d, 00, 1e, 09, 00, 1e, 00, 1f, 00, 01, 10, 01, 0a, 11, 03, 0d, 00, 0e, 0d, 00, 12, 00, 17, 11, 01, 10, 00, 14, 15, 01, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 15, 01, 11, 00, 12, 15, 01, 11, 00, 22, 02, 00, 22, 00, 23, 00, 01, 14, 01, 0e, 0e, 03, 09, 00, 0f, 01, 01, 05, 00, 06]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 6
-- expression 0 operands: lhs = Counter(1), rhs = Zero
-- expression 1 operands: lhs = Counter(7), rhs = Zero
-- expression 2 operands: lhs = Expression(3, Add), rhs = Expression(4, Add)
-- expression 3 operands: lhs = Counter(0), rhs = Counter(6)
-- expression 4 operands: lhs = Counter(4), rhs = Counter(5)
-- expression 5 operands: lhs = Counter(5), rhs = Counter(6)
+Number of expressions: 4
+- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(2, Add)
+- expression 1 operands: lhs = Counter(0), rhs = Counter(4)
+- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 3 operands: lhs = Counter(3), rhs = Counter(4)
 Number of file 0 mappings: 20
 - Code(Counter(0)) at (prev + 9, 5) to (start + 1, 16)
 - Code(Counter(1)) at (prev + 2, 16) to (start + 0, 21)
 - Code(Zero) at (prev + 1, 23) to (start + 0, 27)
 - Code(Zero) at (prev + 0, 28) to (start + 0, 30)
-- Code(Expression(0, Sub)) at (prev + 1, 13) to (start + 0, 14)
-    = (c1 - Zero)
+- Code(Counter(1)) at (prev + 1, 13) to (start + 0, 14)
 - Code(Counter(1)) at (prev + 1, 13) to (start + 0, 30)
-- Code(Counter(4)) at (prev + 0, 30) to (start + 0, 31)
+- Code(Counter(2)) at (prev + 0, 30) to (start + 0, 31)
 - Code(Zero) at (prev + 1, 16) to (start + 1, 10)
-- Code(Counter(6)) at (prev + 3, 13) to (start + 0, 14)
-- Code(Counter(5)) at (prev + 0, 18) to (start + 0, 23)
-- Code(Counter(6)) at (prev + 1, 16) to (start + 0, 20)
-- Code(Counter(7)) at (prev + 1, 20) to (start + 0, 25)
+- Code(Counter(4)) at (prev + 3, 13) to (start + 0, 14)
+- Code(Counter(3)) at (prev + 0, 18) to (start + 0, 23)
+- Code(Counter(4)) at (prev + 1, 16) to (start + 0, 20)
+- Code(Counter(5)) at (prev + 1, 20) to (start + 0, 25)
 - Code(Zero) at (prev + 1, 27) to (start + 0, 31)
 - Code(Zero) at (prev + 0, 32) to (start + 0, 34)
-- Code(Expression(1, Sub)) at (prev + 1, 17) to (start + 0, 18)
-    = (c7 - Zero)
-- Code(Counter(7)) at (prev + 1, 17) to (start + 0, 34)
-- Code(Expression(2, Sub)) at (prev + 0, 34) to (start + 0, 35)
-    = ((c0 + c6) - (c4 + c5))
+- Code(Counter(5)) at (prev + 1, 17) to (start + 0, 18)
+- Code(Counter(5)) at (prev + 1, 17) to (start + 0, 34)
+- Code(Expression(0, Sub)) at (prev + 0, 34) to (start + 0, 35)
+    = ((c0 + c4) - (c2 + c3))
 - Code(Zero) at (prev + 1, 20) to (start + 1, 14)
-- Code(Expression(5, Sub)) at (prev + 3, 9) to (start + 0, 15)
-    = (c5 - c6)
+- Code(Expression(3, Sub)) at (prev + 3, 9) to (start + 0, 15)
+    = (c3 - c4)
 - Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6)
-Highest counter ID seen: c7
+Highest counter ID seen: c5
 
 Function name: <loops_branches::DisplayTest as core::fmt::Display>::fmt
-Raw bytes (122): 0x[01, 01, 09, 01, 00, 01, 00, 0d, 00, 0d, 00, 0d, 00, 1b, 1f, 01, 0d, 09, 1d, 09, 0d, 14, 01, 22, 05, 01, 11, 00, 01, 12, 01, 0a, 02, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 06, 01, 0d, 00, 0e, 02, 01, 0d, 00, 1e, 1d, 00, 1e, 00, 1f, 0d, 02, 0d, 00, 0e, 09, 00, 12, 00, 17, 0d, 01, 10, 00, 15, 00, 00, 16, 01, 0e, 12, 02, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 0e, 01, 11, 00, 12, 12, 01, 11, 00, 22, 16, 00, 22, 00, 23, 22, 03, 09, 00, 0f, 01, 01, 05, 00, 06]
+Raw bytes (112): 0x[01, 01, 04, 07, 0b, 01, 09, 05, 0d, 05, 09, 14, 01, 22, 05, 01, 11, 00, 01, 12, 01, 0a, 01, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 01, 01, 0d, 00, 0e, 01, 01, 0d, 00, 1e, 0d, 00, 1e, 00, 1f, 09, 02, 0d, 00, 0e, 05, 00, 12, 00, 17, 09, 01, 10, 00, 15, 00, 00, 16, 01, 0e, 09, 02, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 09, 01, 11, 00, 12, 09, 01, 11, 00, 22, 02, 00, 22, 00, 23, 0e, 03, 09, 00, 0f, 01, 01, 05, 00, 06]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 9
-- expression 0 operands: lhs = Counter(0), rhs = Zero
-- expression 1 operands: lhs = Counter(0), rhs = Zero
-- expression 2 operands: lhs = Counter(3), rhs = Zero
-- expression 3 operands: lhs = Counter(3), rhs = Zero
-- expression 4 operands: lhs = Counter(3), rhs = Zero
-- expression 5 operands: lhs = Expression(6, Add), rhs = Expression(7, Add)
-- expression 6 operands: lhs = Counter(0), rhs = Counter(3)
-- expression 7 operands: lhs = Counter(2), rhs = Counter(7)
-- expression 8 operands: lhs = Counter(2), rhs = Counter(3)
+Number of expressions: 4
+- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(2, Add)
+- expression 1 operands: lhs = Counter(0), rhs = Counter(2)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(3)
+- expression 3 operands: lhs = Counter(1), rhs = Counter(2)
 Number of file 0 mappings: 20
 - Code(Counter(0)) at (prev + 34, 5) to (start + 1, 17)
 - Code(Zero) at (prev + 1, 18) to (start + 1, 10)
-- Code(Expression(0, Sub)) at (prev + 2, 16) to (start + 0, 21)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 2, 16) to (start + 0, 21)
 - Code(Zero) at (prev + 1, 23) to (start + 0, 27)
 - Code(Zero) at (prev + 0, 28) to (start + 0, 30)
-- Code(Expression(1, Sub)) at (prev + 1, 13) to (start + 0, 14)
-    = (c0 - Zero)
-- Code(Expression(0, Sub)) at (prev + 1, 13) to (start + 0, 30)
-    = (c0 - Zero)
-- Code(Counter(7)) at (prev + 0, 30) to (start + 0, 31)
-- Code(Counter(3)) at (prev + 2, 13) to (start + 0, 14)
-- Code(Counter(2)) at (prev + 0, 18) to (start + 0, 23)
-- Code(Counter(3)) at (prev + 1, 16) to (start + 0, 21)
+- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 14)
+- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 30)
+- Code(Counter(3)) at (prev + 0, 30) to (start + 0, 31)
+- Code(Counter(2)) at (prev + 2, 13) to (start + 0, 14)
+- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 23)
+- Code(Counter(2)) at (prev + 1, 16) to (start + 0, 21)
 - Code(Zero) at (prev + 0, 22) to (start + 1, 14)
-- Code(Expression(4, Sub)) at (prev + 2, 20) to (start + 0, 25)
-    = (c3 - Zero)
+- Code(Counter(2)) at (prev + 2, 20) to (start + 0, 25)
 - Code(Zero) at (prev + 1, 27) to (start + 0, 31)
 - Code(Zero) at (prev + 0, 32) to (start + 0, 34)
-- Code(Expression(3, Sub)) at (prev + 1, 17) to (start + 0, 18)
-    = (c3 - Zero)
-- Code(Expression(4, Sub)) at (prev + 1, 17) to (start + 0, 34)
-    = (c3 - Zero)
-- Code(Expression(5, Sub)) at (prev + 0, 34) to (start + 0, 35)
-    = ((c0 + c3) - (c2 + c7))
-- Code(Expression(8, Sub)) at (prev + 3, 9) to (start + 0, 15)
-    = (c2 - c3)
+- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 18)
+- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 34)
+- Code(Expression(0, Sub)) at (prev + 0, 34) to (start + 0, 35)
+    = ((c0 + c2) - (c1 + c3))
+- Code(Expression(3, Sub)) at (prev + 3, 9) to (start + 0, 15)
+    = (c1 - c2)
 - Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6)
-Highest counter ID seen: c7
+Highest counter ID seen: c3
 
 Function name: loops_branches::main
 Raw bytes (9): 0x[01, 01, 00, 01, 01, 37, 01, 05, 02]
diff --git a/tests/coverage/no_spans_if_not.cov-map b/tests/coverage/no_spans_if_not.cov-map
index 9cb7e6a6cff..6c389a24317 100644
--- a/tests/coverage/no_spans_if_not.cov-map
+++ b/tests/coverage/no_spans_if_not.cov-map
@@ -1,13 +1,11 @@
 Function name: no_spans_if_not::affected_function
-Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 16, 1c, 01, 12, 02, 02, 0d, 00, 0f, 00, 02, 0d, 00, 0f]
+Raw bytes (19): 0x[01, 01, 00, 03, 01, 16, 1c, 01, 12, 01, 02, 0d, 00, 0f, 00, 02, 0d, 00, 0f]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 3
 - Code(Counter(0)) at (prev + 22, 28) to (start + 1, 18)
-- Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 15)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 2, 13) to (start + 0, 15)
 - Code(Zero) at (prev + 2, 13) to (start + 0, 15)
 Highest counter ID seen: c0
 
diff --git a/tests/coverage/tight_inf_loop.cov-map b/tests/coverage/tight_inf_loop.cov-map
index 2d2d59bf013..77a8ffb8358 100644
--- a/tests/coverage/tight_inf_loop.cov-map
+++ b/tests/coverage/tight_inf_loop.cov-map
@@ -1,13 +1,11 @@
 Function name: tight_inf_loop::main
-Raw bytes (21): 0x[01, 01, 01, 01, 00, 03, 01, 01, 01, 01, 0d, 00, 02, 09, 00, 10, 02, 01, 06, 01, 02]
+Raw bytes (19): 0x[01, 01, 00, 03, 01, 01, 01, 01, 0d, 00, 02, 09, 00, 10, 01, 01, 06, 01, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 1
-- expression 0 operands: lhs = Counter(0), rhs = Zero
+Number of expressions: 0
 Number of file 0 mappings: 3
 - Code(Counter(0)) at (prev + 1, 1) to (start + 1, 13)
 - Code(Zero) at (prev + 2, 9) to (start + 0, 16)
-- Code(Expression(0, Sub)) at (prev + 1, 6) to (start + 1, 2)
-    = (c0 - Zero)
+- Code(Counter(0)) at (prev + 1, 6) to (start + 1, 2)
 Highest counter ID seen: c0
 
diff --git a/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff
index 138586300ce..e9f48a85f9c 100644
--- a/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff
+++ b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff
@@ -27,18 +27,15 @@
       }
   
 +     coverage body span: $DIR/branch_match_arms.rs:14:11: 21:2 (#0)
-+     coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) };
-+     coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) };
-+     coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) };
-+     coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1: 15:21 (#0);
-+     coverage Code(Counter(1)) => $DIR/branch_match_arms.rs:16:17: 16:33 (#0);
-+     coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17: 17:33 (#0);
-+     coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:18:17: 18:33 (#0);
-+     coverage Code(Expression(2)) => $DIR/branch_match_arms.rs:19:17: 19:33 (#0);
-+     coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:21:2: 21:2 (#0);
++     coverage Code { bcb: bcb0 } => $DIR/branch_match_arms.rs:14:1: 15:21 (#0);
++     coverage Code { bcb: bcb1 } => $DIR/branch_match_arms.rs:16:17: 16:33 (#0);
++     coverage Code { bcb: bcb3 } => $DIR/branch_match_arms.rs:17:17: 17:33 (#0);
++     coverage Code { bcb: bcb4 } => $DIR/branch_match_arms.rs:18:17: 18:33 (#0);
++     coverage Code { bcb: bcb5 } => $DIR/branch_match_arms.rs:19:17: 19:33 (#0);
++     coverage Code { bcb: bcb2 } => $DIR/branch_match_arms.rs:21:2: 21:2 (#0);
 + 
       bb0: {
-+         Coverage::CounterIncrement(0);
++         Coverage::VirtualCounter(bcb0);
           StorageLive(_1);
           _1 = Enum::A(const 0_u32);
           PlaceMention(_1);
@@ -52,22 +49,22 @@
       }
   
       bb2: {
-+         Coverage::CounterIncrement(1);
++         Coverage::VirtualCounter(bcb1);
           falseEdge -> [real: bb8, imaginary: bb3];
       }
   
       bb3: {
-+         Coverage::CounterIncrement(2);
++         Coverage::VirtualCounter(bcb3);
           falseEdge -> [real: bb7, imaginary: bb4];
       }
   
       bb4: {
-+         Coverage::CounterIncrement(3);
++         Coverage::VirtualCounter(bcb4);
           falseEdge -> [real: bb6, imaginary: bb5];
       }
   
       bb5: {
-+         Coverage::ExpressionUsed(2);
++         Coverage::VirtualCounter(bcb5);
           StorageLive(_9);
           _9 = copy ((_1 as A).0: u32);
           StorageLive(_10);
@@ -124,6 +121,7 @@
       }
   
       bb13: {
++         Coverage::VirtualCounter(bcb2);
           StorageDead(_1);
           return;
       }
diff --git a/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
index a91d88984a8..cbef6de917d 100644
--- a/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
+++ b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
@@ -5,10 +5,10 @@
       let mut _0: bool;
   
 +     coverage body span: $DIR/instrument_coverage.rs:29:18: 31:2 (#0)
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:29:1: 31:2 (#0);
++     coverage Code { bcb: bcb0 } => $DIR/instrument_coverage.rs:29:1: 31:2 (#0);
 + 
       bb0: {
-+         Coverage::CounterIncrement(0);
++         Coverage::VirtualCounter(bcb0);
           _0 = const true;
           return;
       }
diff --git a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
index d7ea442518e..b166d79a412 100644
--- a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
+++ b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
@@ -8,20 +8,19 @@
       let mut _3: !;
   
 +     coverage body span: $DIR/instrument_coverage.rs:14:11: 20:2 (#0)
-+     coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Subtract, rhs: Counter(0) };
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:14:1: 14:11 (#0);
-+     coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:16:12: 16:17 (#0);
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:17:13: 17:18 (#0);
-+     coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:18:10: 18:10 (#0);
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:20:2: 20:2 (#0);
++     coverage Code { bcb: bcb0 } => $DIR/instrument_coverage.rs:14:1: 14:11 (#0);
++     coverage Code { bcb: bcb1 } => $DIR/instrument_coverage.rs:16:12: 16:17 (#0);
++     coverage Code { bcb: bcb2 } => $DIR/instrument_coverage.rs:17:13: 17:18 (#0);
++     coverage Code { bcb: bcb3 } => $DIR/instrument_coverage.rs:18:10: 18:10 (#0);
++     coverage Code { bcb: bcb2 } => $DIR/instrument_coverage.rs:20:2: 20:2 (#0);
 + 
       bb0: {
-+         Coverage::CounterIncrement(0);
++         Coverage::VirtualCounter(bcb0);
           goto -> bb1;
       }
   
       bb1: {
-+         Coverage::CounterIncrement(1);
++         Coverage::VirtualCounter(bcb1);
           falseUnwind -> [real: bb2, unwind: bb6];
       }
   
@@ -35,13 +34,14 @@
       }
   
       bb4: {
++         Coverage::VirtualCounter(bcb2);
           _0 = const ();
           StorageDead(_2);
           return;
       }
   
       bb5: {
-+         Coverage::ExpressionUsed(0);
++         Coverage::VirtualCounter(bcb3);
           _1 = const ();
           StorageDead(_2);
           goto -> bb1;
diff --git a/tests/mir-opt/coverage/instrument_coverage.rs b/tests/mir-opt/coverage/instrument_coverage.rs
index c49786f9615..48647402d0f 100644
--- a/tests/mir-opt/coverage/instrument_coverage.rs
+++ b/tests/mir-opt/coverage/instrument_coverage.rs
@@ -8,9 +8,9 @@
 // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff
 // CHECK-LABEL: fn main()
 // CHECK: coverage body span:
-// CHECK: coverage Code(Counter({{[0-9]+}})) =>
+// CHECK: coverage Code { bcb: bcb{{[0-9]+}} } =>
 // CHECK: bb0:
-// CHECK: Coverage::CounterIncrement
+// CHECK: Coverage::VirtualCounter
 fn main() {
     loop {
         if bar() {
@@ -22,9 +22,9 @@ fn main() {
 // EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff
 // CHECK-LABEL: fn bar()
 // CHECK: coverage body span:
-// CHECK: coverage Code(Counter({{[0-9]+}})) =>
+// CHECK: coverage Code { bcb: bcb{{[0-9]+}} } =>
 // CHECK: bb0:
-// CHECK: Coverage::CounterIncrement
+// CHECK: Coverage::VirtualCounter
 #[inline(never)]
 fn bar() -> bool {
     true
diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff
index 2c7ec6e85eb..855f806aae1 100644
--- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff
+++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff
@@ -8,15 +8,14 @@
       coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0)
   
       coverage body span: $DIR/instrument_coverage_cleanup.rs:13:11: 15:2 (#0)
-      coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) };
-      coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
-      coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
-      coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
-      coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
-      coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);
+      coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
+      coverage Code { bcb: bcb3 } => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
+      coverage Code { bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
+      coverage Code { bcb: bcb2 } => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
+      coverage Branch { true_bcb: bcb3, false_bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);
   
       bb0: {
-          Coverage::CounterIncrement(0);
+          Coverage::VirtualCounter(bcb0);
 -         Coverage::SpanMarker;
 +         nop;
           StorageLive(_1);
@@ -28,7 +27,7 @@
       }
   
       bb2: {
-          Coverage::CounterIncrement(1);
+          Coverage::VirtualCounter(bcb1);
 -         Coverage::BlockMarker(1);
 +         nop;
           _0 = const ();
@@ -36,7 +35,7 @@
       }
   
       bb3: {
-          Coverage::ExpressionUsed(0);
+          Coverage::VirtualCounter(bcb3);
 -         Coverage::BlockMarker(0);
 +         nop;
           _0 = const ();
@@ -44,6 +43,7 @@
       }
   
       bb4: {
+          Coverage::VirtualCounter(bcb2);
           StorageDead(_1);
           return;
       }
diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff
index c08265eb0e9..df1f1e8bc50 100644
--- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff
+++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff
@@ -8,15 +8,14 @@
       coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0)
   
 +     coverage body span: $DIR/instrument_coverage_cleanup.rs:13:11: 15:2 (#0)
-+     coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) };
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
-+     coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
-+     coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
-+     coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);
++     coverage Code { bcb: bcb0 } => $DIR/instrument_coverage_cleanup.rs:13:1: 14:36 (#0);
++     coverage Code { bcb: bcb3 } => $DIR/instrument_coverage_cleanup.rs:14:37: 14:39 (#0);
++     coverage Code { bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:39: 14:39 (#0);
++     coverage Code { bcb: bcb2 } => $DIR/instrument_coverage_cleanup.rs:15:2: 15:2 (#0);
++     coverage Branch { true_bcb: bcb3, false_bcb: bcb1 } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0);
 + 
       bb0: {
-+         Coverage::CounterIncrement(0);
++         Coverage::VirtualCounter(bcb0);
           Coverage::SpanMarker;
           StorageLive(_1);
           _1 = std::hint::black_box::<bool>(const true) -> [return: bb1, unwind: bb5];
@@ -27,20 +26,21 @@
       }
   
       bb2: {
-+         Coverage::CounterIncrement(1);
++         Coverage::VirtualCounter(bcb1);
           Coverage::BlockMarker(1);
           _0 = const ();
           goto -> bb4;
       }
   
       bb3: {
-+         Coverage::ExpressionUsed(0);
++         Coverage::VirtualCounter(bcb3);
           Coverage::BlockMarker(0);
           _0 = const ();
           goto -> bb4;
       }
   
       bb4: {
++         Coverage::VirtualCounter(bcb2);
           StorageDead(_1);
           return;
       }
diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.rs b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs
index acc544a28af..e8af4d6174f 100644
--- a/tests/mir-opt/coverage/instrument_coverage_cleanup.rs
+++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs
@@ -3,7 +3,7 @@
 // but leaves the statements that were added by InstrumentCoverage.
 //
 // Removed statement kinds: BlockMarker, SpanMarker
-// Retained statement kinds: CounterIncrement, ExpressionUsed
+// Retained statement kinds: VirtualCounter
 
 //@ test-mir-pass: InstrumentCoverage
 //@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime
@@ -16,6 +16,6 @@ fn main() {
 
 // CHECK-NOT: Coverage::BlockMarker
 // CHECK-NOT: Coverage::SpanMarker
-// CHECK:     Coverage::CounterIncrement
+// CHECK:     Coverage::VirtualCounter
 // CHECK-NOT: Coverage::BlockMarker
 // CHECK-NOT: Coverage::SpanMarker