about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_arena/src/lib.rs2
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs8
-rw-r--r--compiler/rustc_ast/src/lib.rs3
-rw-r--r--compiler/rustc_ast/src/token.rs2
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl6
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs11
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs10
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs77
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs8
-rw-r--r--compiler/rustc_attr/src/builtin.rs8
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs196
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs172
-rw-r--r--compiler/rustc_borrowck/src/facts.rs3
-rw-r--r--compiler/rustc_borrowck/src/nll.rs42
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs48
-rw-r--r--compiler/rustc_borrowck/src/region_infer/values.rs5
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs82
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs18
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock166
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml16
-rw-r--r--compiler/rustc_codegen_cranelift/Readme.md14
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/build_backend.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/prepare.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/tests.rs67
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/utils.rs35
-rw-r--r--compiler/rustc_codegen_cranelift/config.txt1
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Allow-internal-features.patch24
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0001-regex-Ignore-test-which-gets-miscompiled-with-llvm-sysroot.patch25
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch2
-rw-r--r--compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml72
-rw-r--r--compiler/rustc_codegen_cranelift/patches/rand-lock.toml563
-rw-r--r--compiler/rustc_codegen_cranelift/patches/regex-lock.toml422
-rw-r--r--compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml28
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh18
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/returning.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/allocator.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/analyze.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/src/cast.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/common.rs13
-rw-r--r--compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs16
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/object.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs101
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/jit.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/global_asm.rs23
-rw-r--r--compiler/rustc_codegen_cranelift/src/inline_asm.rs142
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs39
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs10
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs39
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs39
-rw-r--r--compiler/rustc_codegen_cranelift/src/pointer.rs5
-rw-r--r--compiler/rustc_codegen_cranelift/src/pretty_clif.rs22
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs10
-rw-r--r--compiler/rustc_codegen_gcc/.github/workflows/ci.yml18
-rw-r--r--compiler/rustc_codegen_gcc/.github/workflows/release.yml26
-rw-r--r--compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml24
-rw-r--r--compiler/rustc_codegen_gcc/.gitignore1
-rw-r--r--compiler/rustc_codegen_gcc/Cargo.lock261
-rw-r--r--compiler/rustc_codegen_gcc/Cargo.toml6
-rw-r--r--compiler/rustc_codegen_gcc/Readme.md94
-rwxr-xr-xcompiler/rustc_codegen_gcc/build.sh67
-rw-r--r--compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml2
-rwxr-xr-xcompiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh4
-rwxr-xr-xcompiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh39
-rw-r--r--compiler/rustc_codegen_gcc/build_system/Cargo.lock7
-rw-r--r--compiler/rustc_codegen_gcc/build_system/Cargo.toml8
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/build.rs233
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/config.rs125
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/main.rs62
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/prepare.rs227
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/rustc_info.rs12
-rw-r--r--compiler/rustc_codegen_gcc/build_system/src/utils.rs240
-rw-r--r--compiler/rustc_codegen_gcc/config.sh15
-rw-r--r--compiler/rustc_codegen_gcc/doc/add-attribute.md17
-rw-r--r--compiler/rustc_codegen_gcc/doc/gimple.md111
-rw-r--r--compiler/rustc_codegen_gcc/doc/sending-gcc-patch.md44
-rw-r--r--compiler/rustc_codegen_gcc/example/alloc_example.rs1
-rw-r--r--compiler/rustc_codegen_gcc/example/alloc_system.rs2
-rw-r--r--compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs1
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core.rs11
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs2
-rw-r--r--compiler/rustc_codegen_gcc/example/mod_bench.rs1
-rw-r--r--compiler/rustc_codegen_gcc/failing-lto-tests.txt23
-rw-r--r--compiler/rustc_codegen_gcc/failing-non-lto-tests.txt11
-rw-r--r--compiler/rustc_codegen_gcc/failing-ui-tests.txt27
-rw-r--r--compiler/rustc_codegen_gcc/failing-ui-tests12.txt1
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl30
-rw-r--r--compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch19
-rw-r--r--compiler/rustc_codegen_gcc/patches/0001-Disable-examples.patch25
-rwxr-xr-xcompiler/rustc_codegen_gcc/prepare.sh30
-rwxr-xr-xcompiler/rustc_codegen_gcc/prepare_build.sh5
-rw-r--r--compiler/rustc_codegen_gcc/rust-toolchain2
-rwxr-xr-xcompiler/rustc_codegen_gcc/rustup.sh2
-rw-r--r--compiler/rustc_codegen_gcc/src/abi.rs44
-rw-r--r--compiler/rustc_codegen_gcc/src/allocator.rs109
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/attributes.rs102
-rw-r--r--compiler/rustc_codegen_gcc/src/back/lto.rs341
-rw-r--r--compiler/rustc_codegen_gcc/src/back/mod.rs1
-rw-r--r--compiler/rustc_codegen_gcc/src/back/write.rs102
-rw-r--r--compiler/rustc_codegen_gcc/src/base.rs71
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs29
-rw-r--r--compiler/rustc_codegen_gcc/src/declare.rs40
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs87
-rw-r--r--compiler/rustc_codegen_gcc/src/gcc_util.rs223
-rw-r--r--compiler/rustc_codegen_gcc/src/int.rs81
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/archs.rs114
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs20
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/mod.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs192
-rw-r--r--compiler/rustc_codegen_gcc/src/type_of.rs10
-rwxr-xr-xcompiler/rustc_codegen_gcc/test.sh14
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/abort1.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/abort2.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/array.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/asm.rs2
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/assign.rs4
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/closure.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/condition.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/empty_main.rs1
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/exit.rs1
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/exit_code.rs1
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/gep.rs10
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/int_overflow.rs4
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/mut_ref.rs4
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/operations.rs4
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/return-tuple.rs1
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/slice.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/static.rs3
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/structs.rs1
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/tuple.rs1
-rw-r--r--compiler/rustc_codegen_gcc/tools/generate_intrinsics.py8
-rwxr-xr-xcompiler/rustc_codegen_gcc/y.sh8
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs7
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl24
-rw-r--r--compiler/rustc_codegen_ssa/src/assert_module_sources.rs (renamed from compiler/rustc_incremental/src/assert_module_sources.rs)138
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs69
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs4
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs14
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs55
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs23
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs24
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs49
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs20
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs38
-rw-r--r--compiler/rustc_data_structures/src/graph/dominators/mod.rs27
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs25
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs408
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0706.md6
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs1
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_feature/src/accepted.rs8
-rw-r--r--compiler/rustc_feature/src/active.rs10
-rw-r--r--compiler/rustc_feature/src/removed.rs2
-rw-r--r--compiler/rustc_fluent_macro/src/lib.rs3
-rw-r--r--compiler/rustc_graphviz/src/lib.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs3
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs191
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs20
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs72
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs12
-rw-r--r--compiler/rustc_incremental/messages.ftl14
-rw-r--r--compiler/rustc_incremental/src/errors.rs50
-rw-r--r--compiler/rustc_incremental/src/lib.rs4
-rw-r--r--compiler/rustc_infer/src/infer/equate.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs508
-rw-r--r--compiler/rustc_infer/src/infer/generalize.rs2
-rw-r--r--compiler/rustc_infer/src/lib.rs3
-rw-r--r--compiler/rustc_interface/src/passes.rs3
-rw-r--r--compiler/rustc_interface/src/tests.rs3
-rw-r--r--compiler/rustc_lint/src/async_fn_in_trait.rs3
-rw-r--r--compiler/rustc_lint/src/builtin.rs12
-rw-r--r--compiler/rustc_lint/src/context.rs17
-rw-r--r--compiler/rustc_lint/src/lib.rs7
-rw-r--r--compiler/rustc_lint/src/lints.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs17
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs41
-rw-r--r--compiler/rustc_llvm/src/lib.rs3
-rw-r--r--compiler/rustc_log/src/lib.rs11
-rw-r--r--compiler/rustc_metadata/src/lib.rs3
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs11
-rw-r--r--compiler/rustc_middle/src/lib.rs2
-rw-r--r--compiler/rustc_middle/src/thir.rs13
-rw-r--r--compiler/rustc_middle/src/ty/context.rs25
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs65
-rw-r--r--compiler/rustc_mir_build/messages.ftl2
-rw-r--r--compiler/rustc_mir_build/src/errors.rs1
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs8
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs85
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs1117
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs267
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs129
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs21
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs57
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs131
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs277
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs268
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs184
-rw-r--r--compiler/rustc_mir_transform/src/coverage/tests.rs4
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs17
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs1
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs37
-rw-r--r--compiler/rustc_mir_transform/src/large_enums.rs3
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs4
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs25
-rw-r--r--compiler/rustc_mir_transform/src/lower_slice_len.rs79
-rw-r--r--compiler/rustc_mir_transform/src/nrvo.rs2
-rw-r--r--compiler/rustc_mir_transform/src/ssa.rs77
-rw-r--r--compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs25
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs16
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs59
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs20
-rw-r--r--compiler/rustc_parse_format/src/lib.rs3
-rw-r--r--compiler/rustc_passes/messages.ftl2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs13
-rw-r--r--compiler/rustc_passes/src/errors.rs10
-rw-r--r--compiler/rustc_passes/src/lib.rs3
-rw-r--r--compiler/rustc_passes/src/loops.rs55
-rw-r--r--compiler/rustc_plugin_impl/src/lib.rs3
-rw-r--r--compiler/rustc_privacy/src/lib.rs3
-rw-r--r--compiler/rustc_query_impl/src/lib.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs2
-rw-r--r--compiler/rustc_serialize/src/lib.rs3
-rw-r--r--compiler/rustc_session/Cargo.toml1
-rw-r--r--compiler/rustc_session/messages.ftl9
-rw-r--r--compiler/rustc_session/src/cgu_reuse_tracker.rs136
-rw-r--r--compiler/rustc_session/src/config.rs172
-rw-r--r--compiler/rustc_session/src/errors.rs19
-rw-r--r--compiler/rustc_session/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/options.rs27
-rw-r--r--compiler/rustc_session/src/session.rs11
-rw-r--r--compiler/rustc_smir/Cargo.toml1
-rw-r--r--compiler/rustc_smir/src/lib.rs3
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs72
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs49
-rw-r--r--compiler/rustc_span/src/lib.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs3
-rw-r--r--compiler/rustc_target/src/lib.rs2
-rw-r--r--compiler/rustc_target/src/spec/mod.rs84
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/eval_ctxt.rs)0
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs (renamed from compiler/rustc_trait_selection/src/solve/inherent_projection.rs)0
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/project_goals.rs)4
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs (renamed from compiler/rustc_trait_selection/src/solve/opaques.rs)2
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs (renamed from compiler/rustc_trait_selection/src/solve/weak_types.rs)0
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph.rs (renamed from compiler/rustc_trait_selection/src/solve/search_graph/mod.rs)0
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs46
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs16
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs3
-rw-r--r--compiler/rustc_type_ir/src/structural_impls.rs6
-rw-r--r--compiler/stable_mir/src/lib.rs49
-rw-r--r--compiler/stable_mir/src/ty.rs43
279 files changed, 7628 insertions, 4504 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index bf8a7eb293e..54274f15356 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -11,6 +11,8 @@
     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(no_crate_inject, attr(deny(warnings)))
 )]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(core_intrinsics)]
 #![feature(dropck_eyepatch)]
 #![feature(new_uninit)]
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index db008ea139d..7b7078fabbb 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -197,10 +197,10 @@ impl Attribute {
                 .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
                 .to_attr_token_stream()
                 .to_tokenstream(),
-            &AttrKind::DocComment(comment_kind, data) => TokenStream::new(vec![TokenTree::Token(
-                Token::new(token::DocComment(comment_kind, self.style, data), self.span),
-                Spacing::Alone,
-            )]),
+            &AttrKind::DocComment(comment_kind, data) => TokenStream::token_alone(
+                token::DocComment(comment_kind, self.style, data),
+                self.span,
+            ),
         }
     }
 }
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index b07ed1d1c74..ddc7c8ee825 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -8,6 +8,9 @@
     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(attr(deny(warnings)))
 )]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(associated_type_bounds)]
 #![feature(box_patterns)]
 #![feature(const_trait_impl)]
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 09bfbd02198..2de398521f5 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -404,7 +404,7 @@ impl Token {
         [DotDot, DotDotDot, DotDotEq].contains(&self.kind)
     }
 
-    pub fn is_op(&self) -> bool {
+    pub fn is_punct(&self) -> bool {
         match self.kind {
             Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | BinOp(_)
             | BinOpEq(_) | At | Dot | DotDot | DotDotDot | DotDotEq | Comma | Semi | Colon
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index aaeef1ff77d..b9fe99ac72f 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -136,12 +136,6 @@ ast_lowering_template_modifier = template modifier
 
 ast_lowering_this_not_async = this is not `async`
 
-ast_lowering_trait_fn_async =
-    functions in traits cannot be declared `async`
-    .label = `async` because of this
-    .note = `async` trait functions are not currently supported
-    .note2 = consider using the `async-trait` crate: https://crates.io/crates/async-trait
-
 ast_lowering_underscore_expr_lhs_assign =
     in expressions, `_` can only be used on the left-hand side of an assignment
     .label = `_` not allowed here
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index a63bd4f8a02..fe0c7d101c1 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -354,17 +354,6 @@ pub struct InclusiveRangeWithNoEnd {
     pub span: Span,
 }
 
-#[derive(Diagnostic, Clone, Copy)]
-#[diag(ast_lowering_trait_fn_async, code = "E0706")]
-#[note]
-#[note(ast_lowering_note2)]
-pub struct TraitFnAsync {
-    #[primary_span]
-    pub fn_span: Span,
-    #[label]
-    pub span: Span,
-}
-
 #[derive(Diagnostic)]
 pub enum BadReturnTypeNotation {
     #[diag(ast_lowering_bad_return_type_notation_inputs)]
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index edc1e2f0b84..5bfc956aba1 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1623,12 +1623,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     .lower_generic_params(bound_generic_params, hir::GenericParamSource::Binder),
                 bounded_ty: self
                     .lower_ty(bounded_ty, &ImplTraitContext::Disallowed(ImplTraitPosition::Bound)),
-                bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| {
-                    self.lower_param_bound(
-                        bound,
-                        &ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
-                    )
-                })),
+                bounds: self.lower_param_bounds(
+                    bounds,
+                    &ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+                ),
                 span: self.lower_span(*span),
                 origin: PredicateOrigin::WhereClause,
             }),
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 7e3ada9c123..92c548ec2ec 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -40,7 +40,7 @@
 #[macro_use]
 extern crate tracing;
 
-use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait, TraitFnAsync};
+use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait};
 
 use rustc_ast::ptr::P;
 use rustc_ast::visit;
@@ -271,8 +271,6 @@ enum ImplTraitPosition {
     ClosureReturn,
     PointerReturn,
     FnTraitReturn,
-    TraitReturn,
-    ImplReturn,
     GenericDefault,
     ConstTy,
     StaticTy,
@@ -302,8 +300,6 @@ impl std::fmt::Display for ImplTraitPosition {
             ImplTraitPosition::ClosureReturn => "closure return types",
             ImplTraitPosition::PointerReturn => "`fn` pointer return types",
             ImplTraitPosition::FnTraitReturn => "`Fn` trait return types",
-            ImplTraitPosition::TraitReturn => "trait method return types",
-            ImplTraitPosition::ImplReturn => "`impl` method return types",
             ImplTraitPosition::GenericDefault => "generic parameter defaults",
             ImplTraitPosition::ConstTy => "const types",
             ImplTraitPosition::StaticTy => "static types",
@@ -334,20 +330,9 @@ impl FnDeclKind {
         matches!(self, FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait)
     }
 
-    fn return_impl_trait_allowed(&self, tcx: TyCtxt<'_>) -> bool {
+    fn return_impl_trait_allowed(&self) -> bool {
         match self {
-            FnDeclKind::Fn | FnDeclKind::Inherent => true,
-            FnDeclKind::Impl if tcx.features().return_position_impl_trait_in_trait => true,
-            FnDeclKind::Trait if tcx.features().return_position_impl_trait_in_trait => true,
-            _ => false,
-        }
-    }
-
-    fn async_fn_allowed(&self, tcx: TyCtxt<'_>) -> bool {
-        match self {
-            FnDeclKind::Fn | FnDeclKind::Inherent => true,
-            FnDeclKind::Impl if tcx.features().async_fn_in_trait => true,
-            FnDeclKind::Trait if tcx.features().async_fn_in_trait => true,
+            FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait => true,
             _ => false,
         }
     }
@@ -1805,53 +1790,30 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             self.lower_ty_direct(&param.ty, &itctx)
         }));
 
-        let output = if let Some((ret_id, span)) = make_ret_async {
-            if !kind.async_fn_allowed(self.tcx) {
-                match kind {
-                    FnDeclKind::Trait | FnDeclKind::Impl => {
-                        self.tcx
-                            .sess
-                            .create_feature_err(
-                                TraitFnAsync { fn_span, span },
-                                sym::async_fn_in_trait,
-                            )
-                            .emit();
-                    }
-                    _ => {
-                        self.tcx.sess.emit_err(TraitFnAsync { fn_span, span });
-                    }
-                }
-            }
-
+        let output = if let Some((ret_id, _span)) = make_ret_async {
             let fn_def_id = self.local_def_id(fn_node_id);
             self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind, fn_span)
         } else {
             match &decl.output {
                 FnRetTy::Ty(ty) => {
-                    let context = if kind.return_impl_trait_allowed(self.tcx) {
+                    let context = if kind.return_impl_trait_allowed() {
                         let fn_def_id = self.local_def_id(fn_node_id);
                         ImplTraitContext::ReturnPositionOpaqueTy {
                             origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
                             fn_kind: kind,
                         }
                     } else {
-                        let position = match kind {
-                            FnDeclKind::Fn | FnDeclKind::Inherent => {
-                                unreachable!("fn should allow in-band lifetimes")
+                        ImplTraitContext::Disallowed(match kind {
+                            FnDeclKind::Fn
+                            | FnDeclKind::Inherent
+                            | FnDeclKind::Trait
+                            | FnDeclKind::Impl => {
+                                unreachable!("fn should allow return-position impl trait in traits")
                             }
                             FnDeclKind::ExternFn => ImplTraitPosition::ExternFnReturn,
                             FnDeclKind::Closure => ImplTraitPosition::ClosureReturn,
                             FnDeclKind::Pointer => ImplTraitPosition::PointerReturn,
-                            FnDeclKind::Trait => ImplTraitPosition::TraitReturn,
-                            FnDeclKind::Impl => ImplTraitPosition::ImplReturn,
-                        };
-                        match kind {
-                            FnDeclKind::Trait | FnDeclKind::Impl => ImplTraitContext::FeatureGated(
-                                position,
-                                sym::return_position_impl_trait_in_trait,
-                            ),
-                            _ => ImplTraitContext::Disallowed(position),
-                        }
+                        })
                     };
                     hir::FnRetTy::Return(self.lower_ty(ty, &context))
                 }
@@ -1924,18 +1886,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let future_bound = this.lower_async_fn_output_type_to_future_bound(
                     output,
                     span,
-                    if let FnDeclKind::Trait = fn_kind
-                        && !this.tcx.features().return_position_impl_trait_in_trait
-                    {
-                        ImplTraitContext::FeatureGated(
-                            ImplTraitPosition::TraitReturn,
-                            sym::return_position_impl_trait_in_trait,
-                        )
-                    } else {
-                        ImplTraitContext::ReturnPositionOpaqueTy {
-                            origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
-                            fn_kind,
-                        }
+                    ImplTraitContext::ReturnPositionOpaqueTy {
+                        origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
+                        fn_kind,
                     },
                 );
                 arena_vec![this; future_bound]
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 8b7e91882fc..e71f421659e 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -148,7 +148,7 @@ pub fn print_crate<'a>(
 
 /// This makes printed token streams look slightly nicer,
 /// and also addresses some specific regressions described in #63896 and #73345.
-fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
+fn space_between(prev: &TokenTree, curr: &TokenTree) -> bool {
     if let TokenTree::Token(token, _) = prev {
         // No space after these tokens, e.g. `x.y`, `$e`
         // (The carets point to `prev`.)       ^     ^
@@ -159,9 +159,9 @@ fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
             return comment_kind != CommentKind::Line;
         }
     }
-    match tt {
+    match curr {
         // No space before these tokens, e.g. `foo,`, `println!`, `x.y`
-        // (The carets point to `token`.)         ^           ^     ^
+        // (The carets point to `curr`.)          ^           ^     ^
         //
         // FIXME: having `Not` here works well for macro invocations like
         // `println!()`, but is bad when `!` means "logical not" or "the never
@@ -575,7 +575,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         while let Some(tt) = iter.next() {
             self.print_tt(tt, convert_dollar_crate);
             if let Some(next) = iter.peek() {
-                if tt_prepend_space(next, tt) {
+                if space_between(tt, next) {
                     self.space();
                 }
             }
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index f013ff45a4f..76659458dad 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -548,7 +548,11 @@ pub fn cfg_matches(
                     UNEXPECTED_CFGS,
                     cfg.span,
                     lint_node_id,
-                    "unexpected `cfg` condition value",
+                    if let Some(value) = cfg.value {
+                        format!("unexpected `cfg` condition value: `{value}`")
+                    } else {
+                        format!("unexpected `cfg` condition value: (none)")
+                    },
                     BuiltinLintDiagnostics::UnexpectedCfgValue(
                         (cfg.name, cfg.name_span),
                         cfg.value.map(|v| (v, cfg.value_span.unwrap())),
@@ -560,7 +564,7 @@ pub fn cfg_matches(
                     UNEXPECTED_CFGS,
                     cfg.span,
                     lint_node_id,
-                    "unexpected `cfg` condition name",
+                    format!("unexpected `cfg` condition name: `{}`", cfg.name),
                     BuiltinLintDiagnostics::UnexpectedCfgName(
                         (cfg.name, cfg.name_span),
                         cfg.value.map(|v| (v, cfg.value_span.unwrap())),
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 4ac633c263f..6ea84620bbe 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -1,6 +1,7 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::graph::WithSuccessors;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::{
     self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
@@ -222,6 +223,7 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
     }
 }
 
+// This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`.
 pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
@@ -238,15 +240,196 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
     prec.borrows_out_of_scope_at_location
 }
 
+struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
+    visited: BitSet<mir::BasicBlock>,
+    visit_stack: Vec<mir::BasicBlock>,
+    body: &'a Body<'tcx>,
+    regioncx: &'a RegionInferenceContext<'tcx>,
+
+    loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
+}
+
+impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
+    fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
+        Self {
+            visited: BitSet::new_empty(body.basic_blocks.len()),
+            visit_stack: vec![],
+            body,
+            regioncx,
+            loans_out_of_scope_at_location: FxIndexMap::default(),
+        }
+    }
+}
+
+impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
+    /// Loans are in scope while they are live: whether they are contained within any live region.
+    /// In the location-insensitive analysis, a loan will be contained in a region if the issuing
+    /// region can reach it in the subset graph. So this is a reachability problem.
+    fn precompute_loans_out_of_scope(
+        &mut self,
+        loan_idx: BorrowIndex,
+        issuing_region: RegionVid,
+        loan_issued_at: Location,
+    ) {
+        let sccs = self.regioncx.constraint_sccs();
+        let issuing_region_scc = sccs.scc(issuing_region);
+
+        // We first handle the cases where the loan doesn't go out of scope, depending on the issuing
+        // region's successors.
+        for scc in sccs.depth_first_search(issuing_region_scc) {
+            // 1. Via member constraints
+            //
+            // The issuing region can flow into the choice regions, and they are either:
+            // - placeholders or free regions themselves,
+            // - or also transitively outlive a free region.
+            //
+            // That is to say, if there are member constraints here, the loan escapes the function
+            // and cannot go out of scope. We can early return.
+            if self.regioncx.scc_has_member_constraints(scc) {
+                return;
+            }
+
+            // 2. Via regions that are live at all points: placeholders and free regions.
+            //
+            // If the issuing region outlives such a region, its loan escapes the function and
+            // cannot go out of scope. We can early return.
+            if self.regioncx.scc_is_live_at_all_points(scc) {
+                return;
+            }
+        }
+
+        let first_block = loan_issued_at.block;
+        let first_bb_data = &self.body.basic_blocks[first_block];
+
+        // The first block we visit is the one where the loan is issued, starting from the statement
+        // where the loan is issued: at `loan_issued_at`.
+        let first_lo = loan_issued_at.statement_index;
+        let first_hi = first_bb_data.statements.len();
+
+        if let Some(kill_location) =
+            self.loan_kill_location(loan_idx, loan_issued_at, first_block, first_lo, first_hi)
+        {
+            debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
+            self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
+
+            // The loan dies within the first block, we're done and can early return.
+            return;
+        }
+
+        // The loan is not dead. Add successor BBs to the work list, if necessary.
+        for succ_bb in first_bb_data.terminator().successors() {
+            if self.visited.insert(succ_bb) {
+                self.visit_stack.push(succ_bb);
+            }
+        }
+
+        // We may end up visiting `first_block` again. This is not an issue: we know at this point
+        // that the loan is not killed in the `first_lo..=first_hi` range, so checking the
+        // `0..first_lo` range and the `0..first_hi` range gives the same result.
+        while let Some(block) = self.visit_stack.pop() {
+            let bb_data = &self.body[block];
+            let num_stmts = bb_data.statements.len();
+            if let Some(kill_location) =
+                self.loan_kill_location(loan_idx, loan_issued_at, block, 0, num_stmts)
+            {
+                debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
+                self.loans_out_of_scope_at_location
+                    .entry(kill_location)
+                    .or_default()
+                    .push(loan_idx);
+
+                // The loan dies within this block, so we don't need to visit its successors.
+                continue;
+            }
+
+            // Add successor BBs to the work list, if necessary.
+            for succ_bb in bb_data.terminator().successors() {
+                if self.visited.insert(succ_bb) {
+                    self.visit_stack.push(succ_bb);
+                }
+            }
+        }
+
+        self.visited.clear();
+        assert!(self.visit_stack.is_empty(), "visit stack should be empty");
+    }
+
+    /// Returns the lowest statement in `start..=end`, where the loan goes out of scope, if any.
+    /// This is the statement where the issuing region can't reach any of the regions that are live
+    /// at this point.
+    fn loan_kill_location(
+        &self,
+        loan_idx: BorrowIndex,
+        loan_issued_at: Location,
+        block: BasicBlock,
+        start: usize,
+        end: usize,
+    ) -> Option<Location> {
+        for statement_index in start..=end {
+            let location = Location { block, statement_index };
+
+            // Check whether the issuing region can reach local regions that are live at this point:
+            // - a loan is always live at its issuing location because it can reach the issuing
+            // region, which is always live at this location.
+            if location == loan_issued_at {
+                continue;
+            }
+
+            // - the loan goes out of scope at `location` if it's not contained within any regions
+            // live at this point.
+            //
+            // FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and `r` is
+            // live at point `q`, then it's guaranteed that `i` would reach `r` at point `q`.
+            // Reachability is location-insensitive, and we could take advantage of that, by jumping
+            // to a further point than just the next statement: we can jump to the furthest point
+            // within the block where `r` is live.
+            if self.regioncx.is_loan_live_at(loan_idx, location) {
+                continue;
+            }
+
+            // No live region is reachable from the issuing region: the loan is killed at this
+            // point.
+            return Some(location);
+        }
+
+        None
+    }
+}
+
 impl<'a, 'tcx> Borrows<'a, 'tcx> {
     pub fn new(
         tcx: TyCtxt<'tcx>,
         body: &'a Body<'tcx>,
-        nonlexical_regioncx: &'a RegionInferenceContext<'tcx>,
+        regioncx: &'a RegionInferenceContext<'tcx>,
         borrow_set: &'a BorrowSet<'tcx>,
     ) -> Self {
-        let borrows_out_of_scope_at_location =
-            calculate_borrows_out_of_scope_at_location(body, nonlexical_regioncx, borrow_set);
+        let mut borrows_out_of_scope_at_location =
+            calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set);
+
+        // The in-tree polonius analysis computes loans going out of scope using the set-of-loans
+        // model, and makes sure they're identical to the existing computation of the set-of-points
+        // model.
+        if tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
+            let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx);
+            for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
+                let issuing_region = loan_data.region;
+                let issued_location = loan_data.reserve_location;
+
+                polonius_prec.precompute_loans_out_of_scope(
+                    loan_idx,
+                    issuing_region,
+                    issued_location,
+                );
+            }
+
+            assert_eq!(
+                borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location,
+                "the loans out of scope must be the same as the borrows out of scope"
+            );
+
+            borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location;
+        }
+
         Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
     }
 
@@ -333,6 +516,13 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
     }
 }
 
+/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
+/// - we gen the introduced loans
+/// - we kill loans on locals going out of (regular) scope
+/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
+///   region stops containing the CFG points reachable from the issuing location.
+/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
+///   `a.b.c` when `a` is overwritten.
 impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
     type Idx = BorrowIndex;
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 4488276e0e7..ec9bb215f3f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1329,42 +1329,168 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             issue_span: Span,
             expr_span: Span,
             body_expr: Option<&'hir hir::Expr<'hir>>,
-            loop_bind: Option<Symbol>,
+            loop_bind: Option<&'hir Ident>,
+            loop_span: Option<Span>,
+            head_span: Option<Span>,
+            pat_span: Option<Span>,
+            head: Option<&'hir hir::Expr<'hir>>,
         }
         impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
             fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
-                if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind &&
-                    let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind &&
-                    let hir::ExprKind::Call(path, _args) = call.kind &&
-                    let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind &&
-                    let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind &&
-                    let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path &&
-                    let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field &&
-                    self.issue_span.source_equal(call.span) {
-                        self.loop_bind = Some(ident.name);
+                // Try to find
+                // let result = match IntoIterator::into_iter(<head>) {
+                //     mut iter => {
+                //         [opt_ident]: loop {
+                //             match Iterator::next(&mut iter) {
+                //                 None => break,
+                //                 Some(<pat>) => <body>,
+                //             };
+                //         }
+                //     }
+                // };
+                // corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`.
+                if let hir::ExprKind::Call(path, [arg]) = ex.kind
+                    && let hir::ExprKind::Path(
+                        hir::QPath::LangItem(LangItem::IntoIterIntoIter, _, _),
+                    ) = path.kind
+                    && arg.span.contains(self.issue_span)
+                {
+                    // Find `IntoIterator::into_iter(<head>)`
+                    self.head = Some(arg);
+                }
+                if let hir::ExprKind::Loop(
+                    hir::Block { stmts: [stmt, ..], .. },
+                    _,
+                    hir::LoopSource::ForLoop,
+                    _,
+                ) = ex.kind
+                    && let hir::StmtKind::Expr(hir::Expr {
+                        kind: hir::ExprKind::Match(call, [_, bind, ..], _),
+                        span: head_span,
+                        ..
+                    }) = stmt.kind
+                    && let hir::ExprKind::Call(path, _args) = call.kind
+                    && let hir::ExprKind::Path(
+                        hir::QPath::LangItem(LangItem::IteratorNext, _, _),
+                    ) = path.kind
+                    && let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind
+                    && let hir::QPath::LangItem(LangItem::OptionSome, pat_span, _) = path
+                    && call.span.contains(self.issue_span)
+                {
+                    // Find `<pat>` and the span for the whole `for` loop.
+                    if let PatField { pat: hir::Pat {
+                        kind: hir::PatKind::Binding(_, _, ident, ..),
+                        ..
+                    }, ..} = field {
+                        self.loop_bind = Some(ident);
                     }
+                    self.head_span = Some(*head_span);
+                    self.pat_span = Some(pat_span);
+                    self.loop_span = Some(stmt.span);
+                }
 
-                if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind &&
-                    body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) {
-                        self.body_expr = Some(ex);
+                if let hir::ExprKind::MethodCall(body_call, recv, ..) = ex.kind
+                    && body_call.ident.name == sym::next
+                    && recv.span.source_equal(self.expr_span)
+                {
+                    self.body_expr = Some(ex);
                 }
 
                 hir::intravisit::walk_expr(self, ex);
             }
         }
-        let mut finder =
-            ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None };
+        let mut finder = ExprFinder {
+            expr_span: span,
+            issue_span,
+            loop_bind: None,
+            body_expr: None,
+            head_span: None,
+            loop_span: None,
+            pat_span: None,
+            head: None,
+        };
         finder.visit_expr(hir.body(body_id).value);
 
-        if let Some(loop_bind) = finder.loop_bind &&
-            let Some(body_expr) = finder.body_expr &&
-                let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) &&
-                let Some(trait_did) = tcx.trait_of_item(def_id) &&
-                tcx.is_diagnostic_item(sym::Iterator, trait_did) {
-                    err.note(format!(
-                        "a for loop advances the iterator for you, the result is stored in `{loop_bind}`."
+        if let Some(body_expr) = finder.body_expr
+            && let Some(loop_span) = finder.loop_span
+            && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id)
+            && let Some(trait_did) = tcx.trait_of_item(def_id)
+            && tcx.is_diagnostic_item(sym::Iterator, trait_did)
+        {
+            if let Some(loop_bind) = finder.loop_bind {
+                err.note(format!(
+                    "a for loop advances the iterator for you, the result is stored in `{}`",
+                    loop_bind.name,
+                ));
+            } else {
+                err.note(
+                    "a for loop advances the iterator for you, the result is stored in its pattern",
+                );
+            }
+            let msg = "if you want to call `next` on a iterator within the loop, consider using \
+                       `while let`";
+            if let Some(head) = finder.head
+                && let Some(pat_span) = finder.pat_span
+                && loop_span.contains(body_expr.span)
+                && loop_span.contains(head.span)
+            {
+                let sm = self.infcx.tcx.sess.source_map();
+
+                let mut sugg = vec![];
+                if let hir::ExprKind::Path(hir::QPath::Resolved(None, _)) = head.kind {
+                    // A bare path doesn't need a `let` assignment, it's already a simple
+                    // binding access.
+                    // As a new binding wasn't added, we don't need to modify the advancing call.
+                    sugg.push((
+                        loop_span.with_hi(pat_span.lo()),
+                        format!("while let Some("),
+                    ));
+                    sugg.push((
+                        pat_span.shrink_to_hi().with_hi(head.span.lo()),
+                        ") = ".to_string(),
+                    ));
+                    sugg.push((
+                        head.span.shrink_to_hi(),
+                        ".next()".to_string(),
+                    ));
+                } else {
+                    // Needs a new a `let` binding.
+                    let indent = if let Some(indent) = sm.indentation_before(loop_span) {
+                        format!("\n{indent}")
+                    } else {
+                        " ".to_string()
+                    };
+                    let Ok(head_str) = sm.span_to_snippet(head.span) else {
+                        err.help(msg);
+                        return;
+                    };
+                    sugg.push((
+                        loop_span.with_hi(pat_span.lo()),
+                        format!("let iter = {head_str};{indent}while let Some("),
                     ));
-                    err.help("if you want to call `next` on an iterator within the loop, consider using `while let`.");
+                    sugg.push((
+                        pat_span.shrink_to_hi().with_hi(head.span.hi()),
+                        ") = iter.next()".to_string(),
+                    ));
+                    // As a new binding was added, we should change how the iterator is advanced to
+                    // use the newly introduced binding.
+                    if let hir::ExprKind::MethodCall(_, recv, ..) = body_expr.kind
+                        && let hir::ExprKind::Path(hir::QPath::Resolved(None, ..)) = recv.kind
+                    {
+                        // As we introduced a `let iter = <head>;`, we need to change where the
+                        // already borrowed value was accessed from `<recv>.next()` to
+                        // `iter.next()`.
+                        sugg.push((recv.span, "iter".to_string()));
+                    }
+                }
+                err.multipart_suggestion(
+                    msg,
+                    sugg,
+                    Applicability::MaybeIncorrect,
+                );
+            } else {
+                err.help(msg);
+            }
         }
     }
 
diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs
index 9916ebca32f..c54e7070478 100644
--- a/compiler/rustc_borrowck/src/facts.rs
+++ b/compiler/rustc_borrowck/src/facts.rs
@@ -41,7 +41,8 @@ pub(crate) trait AllFactsExt {
 impl AllFactsExt for AllFacts {
     /// Return
     fn enabled(tcx: TyCtxt<'_>) -> bool {
-        tcx.sess.opts.unstable_opts.nll_facts || tcx.sess.opts.unstable_opts.polonius
+        tcx.sess.opts.unstable_opts.nll_facts
+            || tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled()
     }
 
     fn write_to_dir(
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 3f60f5aca71..0ea4401a878 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -169,10 +169,11 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
     upvars: &[Upvar<'tcx>],
     consumer_options: Option<ConsumerOptions>,
 ) -> NllOutput<'tcx> {
+    let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
     let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
-        || infcx.tcx.sess.opts.unstable_opts.polonius;
+        || is_polonius_legacy_enabled;
     let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
-        || infcx.tcx.sess.opts.unstable_opts.polonius;
+        || is_polonius_legacy_enabled;
     let mut all_facts =
         (polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default());
 
@@ -181,22 +182,26 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
     let elements = &Rc::new(RegionValueElements::new(&body));
 
     // Run the MIR type-checker.
-    let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
-        type_check::type_check(
-            infcx,
-            param_env,
-            body,
-            promoted,
-            &universal_regions,
-            location_table,
-            borrow_set,
-            &mut all_facts,
-            flow_inits,
-            move_data,
-            elements,
-            upvars,
-            polonius_input,
-        );
+    let MirTypeckResults {
+        constraints,
+        universal_region_relations,
+        opaque_type_values,
+        live_loans,
+    } = type_check::type_check(
+        infcx,
+        param_env,
+        body,
+        promoted,
+        &universal_regions,
+        location_table,
+        borrow_set,
+        &mut all_facts,
+        flow_inits,
+        move_data,
+        elements,
+        upvars,
+        polonius_input,
+    );
 
     if let Some(all_facts) = &mut all_facts {
         let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation");
@@ -274,6 +279,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
         type_tests,
         liveness_constraints,
         elements,
+        live_loans,
     );
 
     // Generate various additional constraints.
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 852935676b6..96cbe98c216 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -7,6 +7,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::graph::scc::Sccs;
 use rustc_errors::Diagnostic;
 use rustc_hir::def_id::CRATE_DEF_ID;
+use rustc_index::bit_set::SparseBitMatrix;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_infer::infer::outlives::test_type_match;
 use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
@@ -21,6 +22,7 @@ use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
 use rustc_span::Span;
 
+use crate::dataflow::BorrowIndex;
 use crate::{
     constraints::{
         graph::NormalConstraintGraph, ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
@@ -30,8 +32,8 @@ use crate::{
     nll::PoloniusOutput,
     region_infer::reverse_sccs::ReverseSccGraph,
     region_infer::values::{
-        LivenessValues, PlaceholderIndices, RegionElement, RegionValueElements, RegionValues,
-        ToElementIndex,
+        LivenessValues, PlaceholderIndices, PointIndex, RegionElement, RegionValueElements,
+        RegionValues, ToElementIndex,
     },
     type_check::{free_region_relations::UniversalRegionRelations, Locations},
     universal_regions::UniversalRegions,
@@ -119,6 +121,9 @@ pub struct RegionInferenceContext<'tcx> {
     /// Information about how the universally quantified regions in
     /// scope on this function relate to one another.
     universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
+
+    /// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`.
+    live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
 }
 
 /// Each time that `apply_member_constraint` is successful, it appends
@@ -330,6 +335,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         type_tests: Vec<TypeTest<'tcx>>,
         liveness_constraints: LivenessValues<RegionVid>,
         elements: &Rc<RegionValueElements>,
+        live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
     ) -> Self {
         debug!("universal_regions: {:#?}", universal_regions);
         debug!("outlives constraints: {:#?}", outlives_constraints);
@@ -383,6 +389,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             type_tests,
             universal_regions,
             universal_region_relations,
+            live_loans,
         };
 
         result.init_free_and_bound_regions();
@@ -683,7 +690,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // In Polonius mode, the errors about missing universal region relations are in the output
         // and need to be emitted or propagated. Otherwise, we need to check whether the
         // constraints were too strong, and if so, emit or propagate those errors.
-        if infcx.tcx.sess.opts.unstable_opts.polonius {
+        if infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled() {
             self.check_polonius_subset_errors(
                 outlives_requirements.as_mut(),
                 &mut errors_buffer,
@@ -2279,6 +2286,41 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         }
         None
     }
+
+    /// Access to the SCC constraint graph.
+    pub(crate) fn constraint_sccs(&self) -> &Sccs<RegionVid, ConstraintSccIndex> {
+        self.constraint_sccs.as_ref()
+    }
+
+    /// Returns whether the given SCC has any member constraints.
+    pub(crate) fn scc_has_member_constraints(&self, scc: ConstraintSccIndex) -> bool {
+        self.member_constraints.indices(scc).next().is_some()
+    }
+
+    /// Returns whether the given SCC is live at all points: whether the representative is a
+    /// placeholder or a free region.
+    pub(crate) fn scc_is_live_at_all_points(&self, scc: ConstraintSccIndex) -> bool {
+        // FIXME: there must be a cleaner way to find this information. At least, when
+        // higher-ranked subtyping is abstracted away from the borrowck main path, we'll only
+        // need to check whether this is a universal region.
+        let representative = self.scc_representatives[scc];
+        let origin = self.var_infos[representative].origin;
+        let live_at_all_points = matches!(
+            origin,
+            RegionVariableOrigin::Nll(
+                NllRegionVariableOrigin::Placeholder(_) | NllRegionVariableOrigin::FreeRegion
+            )
+        );
+        live_at_all_points
+    }
+
+    /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing
+    /// region is contained within the type of a variable that is live at this point.
+    /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`.
+    pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool {
+        let point = self.liveness_constraints.point_from_location(location);
+        self.live_loans.contains(point, loan_idx)
+    }
 }
 
 impl<'tcx> RegionDefinition<'tcx> {
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index d205862cd3f..38452df32e9 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -176,6 +176,11 @@ impl<N: Idx> LivenessValues<N> {
     pub(crate) fn region_value_str(&self, r: N) -> String {
         region_value_str(self.get_elements(r).map(RegionElement::Location))
     }
+
+    #[inline]
+    pub(crate) fn point_from_location(&self, location: Location) -> PointIndex {
+        self.elements.point_from_location(location)
+    }
 }
 
 /// Maps from `ty::PlaceholderRegion` values that are used in the rest of
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 5702d39db32..21da05c32dd 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -1,10 +1,11 @@
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
-use rustc_index::bit_set::HybridBitSet;
+use rustc_data_structures::graph::WithSuccessors;
+use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
 use rustc_index::interval::IntervalSet;
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
 use rustc_middle::traits::query::DropckOutlivesResult;
-use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::{RegionVid, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
 use rustc_span::DUMMY_SP;
 use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -14,6 +15,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
 use rustc_mir_dataflow::ResultsCursor;
 
+use crate::dataflow::BorrowIndex;
 use crate::{
     region_infer::values::{self, PointIndex, RegionValueElements},
     type_check::liveness::local_use_map::LocalUseMap,
@@ -50,6 +52,33 @@ pub(super) fn trace<'mir, 'tcx>(
 
     let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body);
 
+    // When using `-Zpolonius=next`, compute the set of loans that can reach a given region.
+    let num_loans = typeck.borrowck_context.borrow_set.len();
+    let mut inflowing_loans = SparseBitMatrix::new(num_loans);
+    if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
+        let borrowck_context = &typeck.borrowck_context;
+        let borrow_set = &borrowck_context.borrow_set;
+        let constraint_set = &borrowck_context.constraints.outlives_constraints;
+
+        let num_region_vars = typeck.infcx.num_region_vars();
+        let graph = constraint_set.graph(num_region_vars);
+        let region_graph =
+            graph.region_graph(&constraint_set, borrowck_context.universal_regions.fr_static);
+
+        // Traverse each issuing region's constraints, and record the loan as flowing into the
+        // outlived region.
+        for (loan, issuing_region_data) in borrow_set.iter_enumerated() {
+            for succ in region_graph.depth_first_search(issuing_region_data.region) {
+                // We don't need to mention that a loan flows into its issuing region.
+                if succ == issuing_region_data.region {
+                    continue;
+                }
+
+                inflowing_loans.insert(succ, loan);
+            }
+        }
+    };
+
     let cx = LivenessContext {
         typeck,
         body,
@@ -58,6 +87,7 @@ pub(super) fn trace<'mir, 'tcx>(
         local_use_map,
         move_data,
         drop_data: FxIndexMap::default(),
+        inflowing_loans,
     };
 
     let mut results = LivenessResults::new(cx);
@@ -95,6 +125,9 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
     /// Index indicating where each variable is assigned, used, or
     /// dropped.
     local_use_map: &'me LocalUseMap,
+
+    /// Set of loans that flow into a given region, when using `-Zpolonius=next`.
+    inflowing_loans: SparseBitMatrix<RegionVid, BorrowIndex>,
 }
 
 struct DropData<'tcx> {
@@ -486,7 +519,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
     ) {
         debug!("add_use_live_facts_for(value={:?})", value);
 
-        Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at)
+        Self::make_all_regions_live(
+            self.elements,
+            &mut self.typeck,
+            value,
+            live_at,
+            &self.inflowing_loans,
+        );
     }
 
     /// Some variable with type `live_ty` is "drop live" at `location`
@@ -537,7 +576,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
         // All things in the `outlives` array may be touched by
         // the destructor and must be live at this point.
         for &kind in &drop_data.dropck_result.kinds {
-            Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at);
+            Self::make_all_regions_live(
+                self.elements,
+                &mut self.typeck,
+                kind,
+                live_at,
+                &self.inflowing_loans,
+            );
 
             polonius::add_drop_of_var_derefs_origin(&mut self.typeck, dropped_local, &kind);
         }
@@ -548,6 +593,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
         typeck: &mut TypeChecker<'_, 'tcx>,
         value: impl TypeVisitable<TyCtxt<'tcx>>,
         live_at: &IntervalSet<PointIndex>,
+        inflowing_loans: &SparseBitMatrix<RegionVid, BorrowIndex>,
     ) {
         debug!("make_all_regions_live(value={:?})", value);
         debug!(
@@ -556,15 +602,35 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
         );
 
         let tcx = typeck.tcx();
+        let borrowck_context = &mut typeck.borrowck_context;
+
+        // When using `-Zpolonius=next`, we want to record the loans that flow into this value's
+        // regions as being live at the given `live_at` points: this will be used to compute the
+        // location where a loan goes out of scope.
+        let num_loans = borrowck_context.borrow_set.len();
+        let mut value_loans = HybridBitSet::new_empty(num_loans);
+
         tcx.for_each_free_region(&value, |live_region| {
-            let live_region_vid =
-                typeck.borrowck_context.universal_regions.to_region_vid(live_region);
-            typeck
-                .borrowck_context
+            let live_region_vid = borrowck_context.universal_regions.to_region_vid(live_region);
+
+            borrowck_context
                 .constraints
                 .liveness_constraints
                 .add_elements(live_region_vid, live_at);
+
+            // There can only be inflowing loans for this region when we are using
+            // `-Zpolonius=next`.
+            if let Some(inflowing) = inflowing_loans.row(live_region_vid) {
+                value_loans.union(inflowing);
+            }
         });
+
+        // Record the loans reaching the value.
+        if !value_loans.is_empty() {
+            for point in live_at.iter() {
+                borrowck_context.live_loans.union_row(point, &value_loans);
+            }
+        }
     }
 
     fn compute_drop_data(
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index e7b1a489f5d..1ec0e62d16a 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -14,6 +14,7 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::lang_items::LangItem;
+use rustc_index::bit_set::SparseBitMatrix;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_infer::infer::canonical::QueryRegionConstraints;
 use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -50,6 +51,8 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::ResultsCursor;
 
+use crate::dataflow::BorrowIndex;
+use crate::region_infer::values::PointIndex;
 use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst};
 use crate::{
     borrow_set::BorrowSet,
@@ -163,6 +166,9 @@ pub(crate) fn type_check<'mir, 'tcx>(
 
     debug!(?normalized_inputs_and_output);
 
+    // When using `-Zpolonius=next`, liveness will record the set of live loans per point.
+    let mut live_loans = SparseBitMatrix::new(borrow_set.len());
+
     let mut borrowck_context = BorrowCheckContext {
         universal_regions,
         location_table,
@@ -170,6 +176,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
         all_facts,
         constraints: &mut constraints,
         upvars,
+        live_loans: &mut live_loans,
     };
 
     let mut checker = TypeChecker::new(
@@ -240,7 +247,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
         })
         .collect();
 
-    MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
+    MirTypeckResults { constraints, universal_region_relations, opaque_type_values, live_loans }
 }
 
 fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
@@ -855,12 +862,21 @@ struct BorrowCheckContext<'a, 'tcx> {
     borrow_set: &'a BorrowSet<'tcx>,
     pub(crate) constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
     upvars: &'a [Upvar<'tcx>],
+
+    /// The set of loans that are live at a given point in the CFG, filled in by `liveness::trace`,
+    /// when using `-Zpolonius=next`.
+    pub(crate) live_loans: &'a mut SparseBitMatrix<PointIndex, BorrowIndex>,
 }
 
+/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions
+/// inference computation.
 pub(crate) struct MirTypeckResults<'tcx> {
     pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
     pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
     pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
+
+    /// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`.
+    pub(crate) live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
 }
 
 /// A collection of region constraints that must be satisfied for the
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index 9302db104b6..7abfcc8c50c 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -85,7 +85,7 @@ pub fn expand_assert<'cx>(
                 DUMMY_SP,
                 Symbol::intern(&format!(
                     "assertion failed: {}",
-                    pprust::expr_to_string(&cond_expr).escape_debug()
+                    pprust::expr_to_string(&cond_expr)
                 )),
             )],
         );
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index af8e43da4ea..7c324421be9 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -15,9 +15,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.66"
+version = "1.0.75"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
 
 [[package]]
 name = "arbitrary"
@@ -26,12 +26,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
 
 [[package]]
-name = "autocfg"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-
-[[package]]
 name = "bitflags"
 version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -39,9 +33,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bumpalo"
-version = "3.11.1"
+version = "3.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
 
 [[package]]
 name = "cfg-if"
@@ -51,18 +45,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec27af72e56235eb326b5bf2de4e70ab7c5ac1fb683a1829595badaf821607fd"
+checksum = "03b9d1a9e776c27ad55d7792a380785d1fe8c2d7b099eed8dbd8f4af2b598192"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2231e12925e6c5f4bc9c95b62a798eea6ed669a95bc3e00f8b2adb3b7b9b7a80"
+checksum = "5528483314c2dd5da438576cd8a9d0b3cedad66fb8a4727f90cd319a81950038"
 dependencies = [
  "bumpalo",
  "cranelift-bforest",
@@ -72,7 +66,7 @@ dependencies = [
  "cranelift-entity",
  "cranelift-isle",
  "gimli",
- "hashbrown 0.13.2",
+ "hashbrown 0.14.0",
  "log",
  "regalloc2",
  "smallvec",
@@ -81,39 +75,39 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "413b00b8dfb3aab85674a534677e7ca08854b503f164a70ec0634fce80996e2c"
+checksum = "0f46a8318163f7682e35b8730ba93c1b586a2da8ce12a0ed545efc1218550f70"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd0feb9ecc8193ef5cb04f494c5bd835e5bfec4bde726e7ac0444fc9dd76229e"
+checksum = "37d1239cfd50eecfaed468d46943f8650e32969591868ad50111613704da6c70"
 
 [[package]]
 name = "cranelift-control"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72eedd2afcf5fee1e042eaaf18d3750e48ad0eca364a9f5971ecfdd5ef85bf71"
+checksum = "bcc530560c8f16cc1d4dd7ea000c56f519c60d1a914977abe849ce555c35a61d"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7af19157be42671073cf8c2a52d6a4ae1e7b11f1dcb4131fede356d9f91c29dd"
+checksum = "f333fa641a9ad2bff0b107767dcb972c18c2bfab7969805a1d7e42449ccb0408"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2dc7636c5fad156be7d9ae691cd1aaecd97326caf2ab534ba168056d56aa76c"
+checksum = "06abf6563015a80f03f8bc4df307d0a81363f4eb73108df3a34f6e66fb6d5307"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -123,15 +117,15 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1111aea4fb6fade5779903f184249a3fc685a799fe4ec59126f9af59c7c2a74"
+checksum = "0eb29d0edc8a5c029ed0f7ca77501f272738e3c410020b4a00f42ffe8ad2a8aa"
 
 [[package]]
 name = "cranelift-jit"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dadf88076317f6286ec77ebbe65978734fb43b6befdc96f52ff4c4c511841644"
+checksum = "d16e8c5e212b1e63658aada17553497e7a259acab61f044d1f185527efa609fb"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -149,9 +143,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6bae8a82dbf82241b1083e57e06870d2c2bdc9852727be99d58477513816953"
+checksum = "d3b5fd273e1a959e920c7a9d790b1646d31acc8782bb549bad5ab85dd2fc9aa7"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -160,9 +154,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ecfc01a634448468a698beac433d98040033046678a0eed3ca39a3a9f63ae86"
+checksum = "006056a7fa920870bad06bf8e1b3033d70cbb7ee625b035efa9d90882a931868"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -171,9 +165,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.98.0"
+version = "0.100.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee14a7276999f0dcaae2de84043e2c2de50820fb89b3db56fab586a4ad26734"
+checksum = "9c8be1b0e7720f30fec31be0c0b0b23caef2a73fa751190c6a251c1362e8f8c9"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -195,35 +189,29 @@ dependencies = [
 
 [[package]]
 name = "equivalent"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "fallible-iterator"
-version = "0.2.0"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
 
 [[package]]
 name = "gimli"
-version = "0.27.2"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
 dependencies = [
  "fallible-iterator",
- "indexmap 1.9.3",
+ "indexmap",
  "stable_deref_trait",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
-name = "hashbrown"
 version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
@@ -236,15 +224,8 @@ name = "hashbrown"
 version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
-
-[[package]]
-name = "indexmap"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
+ "ahash",
 ]
 
 [[package]]
@@ -259,9 +240,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.138"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 
 [[package]]
 name = "libloading"
@@ -275,12 +256,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.17"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
 name = "mach"
@@ -293,27 +271,27 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.5.0"
+version = "2.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
 
 [[package]]
 name = "object"
-version = "0.30.4"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
+checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
 dependencies = [
  "crc32fast",
- "hashbrown 0.13.2",
- "indexmap 1.9.3",
+ "hashbrown 0.14.0",
+ "indexmap",
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.16.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
 name = "regalloc2"
@@ -357,7 +335,7 @@ dependencies = [
  "cranelift-native",
  "cranelift-object",
  "gimli",
- "indexmap 2.0.0",
+ "indexmap",
  "libloading",
  "object",
  "smallvec",
@@ -366,15 +344,15 @@ dependencies = [
 
 [[package]]
 name = "slice-group-by"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec"
+checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
 
 [[package]]
 name = "smallvec"
-version = "1.10.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
 
 [[package]]
 name = "stable_deref_trait"
@@ -396,9 +374,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "wasmtime-jit-icache-coherence"
-version = "11.0.0"
+version = "13.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e34eb67f0829a5614ec54716c8e0c9fe68fab7b9df3686c85f719c9d247f7169"
+checksum = "c6ff5f3707a5e3797deeeeac6ac26b2e1dd32dbc06693c0ab52e8ac4d18ec706"
 dependencies = [
  "cfg-if",
  "libc",
@@ -438,9 +416,9 @@ dependencies = [
 
 [[package]]
 name = "windows-targets"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
  "windows_aarch64_gnullvm",
  "windows_aarch64_msvc",
@@ -453,42 +431,42 @@ dependencies = [
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index 8ded81d7399..28a37b7995b 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -8,15 +8,15 @@ crate-type = ["dylib"]
 
 [dependencies]
 # These have to be in sync with each other
-cranelift-codegen = { version = "0.98", features = ["unwind", "all-arch"] }
-cranelift-frontend = { version = "0.98" }
-cranelift-module = { version = "0.98" }
-cranelift-native = { version = "0.98" }
-cranelift-jit = { version = "0.98", optional = true }
-cranelift-object = { version = "0.98" }
+cranelift-codegen = { version = "0.100", features = ["unwind", "all-arch"] }
+cranelift-frontend = { version = "0.100" }
+cranelift-module = { version = "0.100" }
+cranelift-native = { version = "0.100" }
+cranelift-jit = { version = "0.100", optional = true }
+cranelift-object = { version = "0.100" }
 target-lexicon = "0.12.0"
-gimli = { version = "0.27.2", default-features = false, features = ["write"]}
-object = { version = "0.30.3", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
+gimli = { version = "0.28", default-features = false, features = ["write"]}
+object = { version = "0.32", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
 
 indexmap = "2.0.0"
 libloading = { version = "0.7.3", optional = true }
diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md
index 62eaef359af..6f2027be96d 100644
--- a/compiler/rustc_codegen_cranelift/Readme.md
+++ b/compiler/rustc_codegen_cranelift/Readme.md
@@ -60,18 +60,14 @@ You need to do this steps to successfully compile and use the cranelift backend
 2. Run `python x.py setup` and choose option for compiler (`b`).
 3. Build compiler and necessary tools: `python x.py build --stage=2 compiler library/std src/tools/rustdoc src/tools/rustfmt`
    * (Optional) You can also build cargo by adding `src/tools/cargo` to previous command.
-4. Copy exectutable files from `./build/host/stage2-tools/<your hostname triple>/release`
-to `./build/host/stage2/bin/`. Note that you would need to do this every time you rebuilt `rust` repository.
-5. Copy cargo from another toolchain: `cp $(rustup which cargo) .build/<your hostname triple>/stage2/bin/cargo`
-   * Another option is to build it at step 3 and copy with other executables at step 4.
-6. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`.
-7. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`.
-8. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc.
-
+4. Copy cargo from a nightly toolchain: `cp $(rustup +nightly which cargo) ./build/host/stage2/bin/cargo`. Note that you would need to do this every time you rebuilt `rust` repository.
+5. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`.
+6. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`.
+7. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc.
   * `rustup run stage2 ./y.sh prepare`
   * `rustup run stage2 ./y.sh build`
   * (Optional) run tests: `rustup run stage2 ./y.sh test`
-9. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`.
+8. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`.
 
 ## Configuration
 
diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
index e434c36f992..d90111adf77 100644
--- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
@@ -20,6 +20,8 @@ pub(crate) fn build_backend(
 
     let mut rustflags = rustflags_from_env("RUSTFLAGS");
 
+    rustflags.push("-Zallow-features=rustc_private".to_owned());
+
     if is_ci() {
         // Deny warnings on CI
         rustflags.push("-Dwarnings".to_owned());
diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
index 165296cb4a9..16e7a4bafae 100644
--- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
@@ -122,10 +122,10 @@ impl GitRepo {
         if download_dir.exists() {
             let actual_hash = format!("{:016x}", hash_dir(&download_dir));
             if actual_hash == self.content_hash {
-                println!("[FRESH] {}", download_dir.display());
+                eprintln!("[FRESH] {}", download_dir.display());
                 return;
             } else {
-                println!(
+                eprintln!(
                     "Mismatched content hash for {download_dir}: {actual_hash} != {content_hash}. Downloading again.",
                     download_dir = download_dir.display(),
                     content_hash = self.content_hash,
@@ -150,7 +150,7 @@ impl GitRepo {
 
         let actual_hash = format!("{:016x}", hash_dir(&download_dir));
         if actual_hash != self.content_hash {
-            println!(
+            eprintln!(
                 "Download of {download_dir} failed with mismatched content hash: {actual_hash} != {content_hash}",
                 download_dir = download_dir.display(),
                 content_hash = self.content_hash,
diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs
index e7bd8b1278c..95ff6b75422 100644
--- a/compiler/rustc_codegen_cranelift/build_system/tests.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs
@@ -9,7 +9,7 @@ use crate::path::{Dirs, RelPath};
 use crate::prepare::{apply_patches, GitRepo};
 use crate::rustc_info::get_default_sysroot;
 use crate::shared_utils::rustflags_from_env;
-use crate::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler, LogGroup};
+use crate::utils::{spawn_and_wait, CargoProject, Compiler, LogGroup};
 use crate::{CodegenBackend, SysrootKind};
 
 static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::BUILD.join("example");
@@ -101,13 +101,11 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[
     TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"),
 ];
 
-// FIXME(rust-random/rand#1293): Newer rand versions fail to test on Windows. Update once this is
-// fixed.
 pub(crate) static RAND_REPO: GitRepo = GitRepo::github(
     "rust-random",
     "rand",
-    "50b9a447410860af8d6db9a208c3576886955874",
-    "446203b96054891e",
+    "f3dd0b885c4597b9617ca79987a0dd899ab29fcb",
+    "3f869e4fcd602b66",
     "rand",
 );
 
@@ -116,8 +114,8 @@ pub(crate) static RAND: CargoProject = CargoProject::new(&RAND_REPO.source_dir()
 pub(crate) static REGEX_REPO: GitRepo = GitRepo::github(
     "rust-lang",
     "regex",
-    "32fed9429eafba0ae92a64b01796a0c5a75b88c8",
-    "fcc4df7c5b902633",
+    "061ee815ef2c44101dba7b0b124600fcb03c1912",
+    "dc26aefbeeac03ca",
     "regex",
 );
 
@@ -126,8 +124,8 @@ pub(crate) static REGEX: CargoProject = CargoProject::new(&REGEX_REPO.source_dir
 pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github(
     "rust-lang",
     "portable-simd",
-    "7c7dbe0c505ccbc02ff30c1e37381ab1d47bf46f",
-    "5bcc9c544f6fa7bd",
+    "4825b2a64d765317066948867e8714674419359b",
+    "8b188cc41f5af835",
     "portable-simd",
 );
 
@@ -180,40 +178,6 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
             spawn_and_wait(build_cmd);
         }
     }),
-    TestCase::custom("test.regex-shootout-regex-dna", &|runner| {
-        REGEX_REPO.patch(&runner.dirs);
-
-        REGEX.clean(&runner.dirs);
-
-        let mut build_cmd = REGEX.build(&runner.target_compiler, &runner.dirs);
-        build_cmd.arg("--example").arg("shootout-regex-dna");
-        spawn_and_wait(build_cmd);
-
-        if runner.is_native {
-            let mut run_cmd = REGEX.run(&runner.target_compiler, &runner.dirs);
-            run_cmd.arg("--example").arg("shootout-regex-dna");
-
-            let input = fs::read_to_string(
-                REGEX.source_dir(&runner.dirs).join("examples").join("regexdna-input.txt"),
-            )
-            .unwrap();
-            let expected = fs::read_to_string(
-                REGEX.source_dir(&runner.dirs).join("examples").join("regexdna-output.txt"),
-            )
-            .unwrap();
-
-            let output = spawn_and_wait_with_input(run_cmd, input);
-
-            let output_matches = expected.lines().eq(output.lines());
-            if !output_matches {
-                println!("Output files don't match!");
-                println!("Expected Output:\n{}", expected);
-                println!("Actual Output:\n{}", output);
-
-                std::process::exit(1);
-            }
-        }
-    }),
     TestCase::custom("test.regex", &|runner| {
         REGEX_REPO.patch(&runner.dirs);
 
@@ -223,7 +187,22 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
             let mut run_cmd = REGEX.test(&runner.target_compiler, &runner.dirs);
             // regex-capi and regex-debug don't have any tests. Nor do they contain any code
             // that is useful to test with cg_clif. Skip building them to reduce test time.
-            run_cmd.args(["-p", "regex", "-p", "regex-syntax", "--", "-q"]);
+            run_cmd.args([
+                "-p",
+                "regex",
+                "-p",
+                "regex-syntax",
+                "--release",
+                "--all-targets",
+                "--",
+                "-q",
+            ]);
+            spawn_and_wait(run_cmd);
+
+            let mut run_cmd = REGEX.test(&runner.target_compiler, &runner.dirs);
+            // don't run integration tests for regex-autonata. they take like 2min each without
+            // much extra coverage of simd usage.
+            run_cmd.args(["-p", "regex-automata", "--release", "--lib", "--", "-q"]);
             spawn_and_wait(run_cmd);
         } else {
             eprintln!("Cross-Compiling: Not running tests");
diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs
index 24624cdeab1..9f24c043a50 100644
--- a/compiler/rustc_codegen_cranelift/build_system/utils.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs
@@ -1,8 +1,8 @@
 use std::env;
 use std::fs;
-use std::io::{self, Write};
+use std::io;
 use std::path::{Path, PathBuf};
-use std::process::{self, Command, Stdio};
+use std::process::{self, Command};
 use std::sync::atomic::{AtomicBool, Ordering};
 
 use crate::path::{Dirs, RelPath};
@@ -47,7 +47,7 @@ impl Compiler {
                 self.runner = vec!["wine".to_owned()];
             }
             _ => {
-                println!("Unknown non-native platform");
+                eprintln!("Unknown non-native platform");
             }
         }
     }
@@ -197,7 +197,9 @@ pub(crate) fn try_hard_link(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
 
 #[track_caller]
 pub(crate) fn spawn_and_wait(mut cmd: Command) {
-    if !cmd.spawn().unwrap().wait().unwrap().success() {
+    let status = cmd.spawn().unwrap().wait().unwrap();
+    if !status.success() {
+        eprintln!("{cmd:?} exited with status {:?}", status);
         process::exit(1);
     }
 }
@@ -207,38 +209,17 @@ pub(crate) fn spawn_and_wait(mut cmd: Command) {
 pub(crate) fn retry_spawn_and_wait(tries: u64, mut cmd: Command) {
     for i in 1..tries + 1 {
         if i != 1 {
-            println!("Command failed. Attempt {i}/{tries}:");
+            eprintln!("Command failed. Attempt {i}/{tries}:");
         }
         if cmd.spawn().unwrap().wait().unwrap().success() {
             return;
         }
         std::thread::sleep(std::time::Duration::from_secs(i * 5));
     }
-    println!("The command has failed after {tries} attempts.");
+    eprintln!("The command has failed after {tries} attempts.");
     process::exit(1);
 }
 
-#[track_caller]
-pub(crate) fn spawn_and_wait_with_input(mut cmd: Command, input: String) -> String {
-    let mut child = cmd
-        .stdin(Stdio::piped())
-        .stdout(Stdio::piped())
-        .spawn()
-        .expect("Failed to spawn child process");
-
-    let mut stdin = child.stdin.take().expect("Failed to open stdin");
-    std::thread::spawn(move || {
-        stdin.write_all(input.as_bytes()).expect("Failed to write to stdin");
-    });
-
-    let output = child.wait_with_output().expect("Failed to read stdout");
-    if !output.status.success() {
-        process::exit(1);
-    }
-
-    String::from_utf8(output.stdout).unwrap()
-}
-
 pub(crate) fn remove_dir_if_exists(path: &Path) {
     match fs::remove_dir_all(&path) {
         Ok(()) => {}
diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt
index fa1c9f4259c..7ff805e58d9 100644
--- a/compiler/rustc_codegen_cranelift/config.txt
+++ b/compiler/rustc_codegen_cranelift/config.txt
@@ -46,6 +46,5 @@ aot.issue-59326
 testsuite.extended_sysroot
 test.rust-random/rand
 test.libcore
-test.regex-shootout-regex-dna
 test.regex
 test.portable-simd
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index 34c7e44b288..934e4b1786f 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -685,6 +685,12 @@ pub macro cfg() {
 
 #[rustc_builtin_macro]
 #[rustc_macro_transparency = "semitransparent"]
+pub macro asm() {
+    /* compiler built-in */
+}
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
 pub macro global_asm() {
     /* compiler built-in */
 }
diff --git a/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Allow-internal-features.patch b/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Allow-internal-features.patch
deleted file mode 100644
index 87252df1eab..00000000000
--- a/compiler/rustc_codegen_cranelift/patches/0001-portable-simd-Allow-internal-features.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From fcf75306d88e533b83eaff3f8d0ab9f307e8a84d Mon Sep 17 00:00:00 2001
-From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
-Date: Wed, 9 Aug 2023 10:01:17 +0000
-Subject: [PATCH] Allow internal features
-
----
- crates/core_simd/src/lib.rs | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs
-index fde406b..b386116 100644
---- a/crates/core_simd/src/lib.rs
-+++ b/crates/core_simd/src/lib.rs
-@@ -19,6 +19,7 @@
- #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really
- #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)]
- #![unstable(feature = "portable_simd", issue = "86656")]
-+#![allow(internal_features)]
- //! Portable SIMD module.
- 
- #[path = "mod.rs"]
--- 
-2.34.1
-
diff --git a/compiler/rustc_codegen_cranelift/patches/0001-regex-Ignore-test-which-gets-miscompiled-with-llvm-sysroot.patch b/compiler/rustc_codegen_cranelift/patches/0001-regex-Ignore-test-which-gets-miscompiled-with-llvm-sysroot.patch
new file mode 100644
index 00000000000..e6ebdcec783
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/patches/0001-regex-Ignore-test-which-gets-miscompiled-with-llvm-sysroot.patch
@@ -0,0 +1,25 @@
+From 5d4afb8d807d181038b6a004d17ed055a8d191b2 Mon Sep 17 00:00:00 2001
+From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
+Date: Mon, 2 Oct 2023 13:59:00 +0000
+Subject: [PATCH] Ignore test which gets miscompiled with llvm sysroot
+
+---
+ regex-automata/src/util/pool.rs | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/regex-automata/src/util/pool.rs b/regex-automata/src/util/pool.rs
+index c03d7b0..28b233b 100644
+--- a/regex-automata/src/util/pool.rs
++++ b/regex-automata/src/util/pool.rs
+@@ -1081,6 +1081,8 @@ mod tests {
+     // into the pool. This in turn resulted in this test producing a data race.
+     #[cfg(feature = "std")]
+     #[test]
++    // FIXME(rustc_codegen_cranelift#1395) miscompilation of thread::scope with LLVM sysroot
++    #[ignore]
+     fn thread_owner_sync() {
+         let pool = Pool::new(|| vec!['a']);
+         {
+-- 
+2.34.1
+
diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch
index a650e10110b..be29ae09bcf 100644
--- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch
+++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch
@@ -19,9 +19,9 @@ index 897a5e9..331f66f 100644
  #![feature(const_option_ext)]
  #![feature(const_result)]
 -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
+ #![cfg_attr(test, feature(cfg_match))]
  #![feature(int_roundings)]
  #![feature(slice_group_by)]
- #![feature(split_array)]
 diff --git a/atomic.rs b/atomic.rs
 index b735957..ea728b6 100644
 --- a/atomic.rs
diff --git a/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml b/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml
index e7db1fd2c7f..5c9dc7b361f 100644
--- a/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml
+++ b/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml
@@ -16,9 +16,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bumpalo"
-version = "3.13.0"
+version = "3.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
 
 [[package]]
 name = "byteorder"
@@ -55,33 +55,33 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.63"
+version = "0.3.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
 dependencies = [
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "log"
-version = "0.4.18"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
 name = "num-traits"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.17.2"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
 name = "ppv-lite86"
@@ -91,9 +91,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.59"
+version = "1.0.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
 dependencies = [
  "unicode-ident",
 ]
@@ -114,9 +114,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.28"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
@@ -181,9 +181,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.18"
+version = "2.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -199,15 +199,15 @@ dependencies = [
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.9"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.86"
+version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -215,9 +215,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.86"
+version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
 dependencies = [
  "bumpalo",
  "log",
@@ -230,9 +230,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.36"
+version = "0.4.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e"
+checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -242,9 +242,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.86"
+version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -252,9 +252,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.86"
+version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -265,15 +265,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.86"
+version = "0.2.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
 
 [[package]]
 name = "wasm-bindgen-test"
-version = "0.3.36"
+version = "0.3.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9e636f3a428ff62b3742ebc3c70e254dfe12b8c2b469d688ea59cdd4abcf502"
+checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671"
 dependencies = [
  "console_error_panic_hook",
  "js-sys",
@@ -285,9 +285,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-test-macro"
-version = "0.3.36"
+version = "0.3.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f18c1fad2f7c4958e7bcce014fa212f59a65d5e3721d0f77e6c0b27ede936ba3"
+checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -295,9 +295,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.63"
+version = "0.3.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
diff --git a/compiler/rustc_codegen_cranelift/patches/rand-lock.toml b/compiler/rustc_codegen_cranelift/patches/rand-lock.toml
index 66c515731c5..aacf3653c16 100644
--- a/compiler/rustc_codegen_cranelift/patches/rand-lock.toml
+++ b/compiler/rustc_codegen_cranelift/patches/rand-lock.toml
@@ -3,6 +3,32 @@
 version = 3
 
 [[package]]
+name = "aho-corasick"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f2135563fb5c609d2b2b87c1e8ce7bc41b0b45430fa9661f457981503dd5bf0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi 0.1.19",
+ "libc",
+ "winapi",
+]
+
+[[package]]
 name = "autocfg"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -29,12 +55,114 @@ dependencies = [
 ]
 
 [[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bumpalo"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
+name = "ciborium"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
+[[package]]
+name = "clap"
+version = "3.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
+dependencies = [
+ "bitflags",
+ "clap_lex",
+ "indexmap",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "criterion"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
+dependencies = [
+ "anes",
+ "atty",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "itertools",
+ "lazy_static",
+ "num-traits",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
+dependencies = [
+ "cast",
+ "itertools",
+]
+
+[[package]]
 name = "crossbeam-channel"
 version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -57,9 +185,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.14"
+version = "0.9.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
 dependencies = [
  "autocfg",
  "cfg-if",
@@ -70,14 +198,49 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.15"
+version = "0.8.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
+name = "darling"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
 name = "easy-cast"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -88,9 +251,9 @@ dependencies = [
 
 [[package]]
 name = "either"
-version = "1.8.1"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 
 [[package]]
 name = "float-ord"
@@ -99,10 +262,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
 
 [[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
 name = "getrandom"
-version = "0.2.9"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
 dependencies = [
  "cfg-if",
  "libc",
@@ -110,25 +279,83 @@ dependencies = [
 ]
 
 [[package]]
+name = "half"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
 name = "hermit-abi"
-version = "0.2.6"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 dependencies = [
  "libc",
 ]
 
 [[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
 name = "itoa"
-version = "1.0.6"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.144"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 
 [[package]]
 name = "libm"
@@ -138,24 +365,30 @@ checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
 
 [[package]]
 name = "log"
-version = "0.4.18"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
 
 [[package]]
 name = "memoffset"
-version = "0.8.0"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "num-traits"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
 dependencies = [
  "autocfg",
  "libm",
@@ -163,15 +396,61 @@ dependencies = [
 
 [[package]]
 name = "num_cpus"
-version = "1.15.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.2",
  "libc",
 ]
 
 [[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "oorandom"
+version = "11.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
+
+[[package]]
+name = "plotters"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
 name = "ppv-lite86"
 version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -179,18 +458,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.59"
+version = "1.0.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.28"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
@@ -200,6 +479,7 @@ name = "rand"
 version = "0.9.0"
 dependencies = [
  "bincode",
+ "criterion",
  "libc",
  "log",
  "rand_chacha",
@@ -236,6 +516,7 @@ dependencies = [
  "rand",
  "rand_pcg",
  "serde",
+ "serde_with",
  "special",
 ]
 
@@ -271,42 +552,80 @@ dependencies = [
 ]
 
 [[package]]
+name = "regex"
+version = "1.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
+
+[[package]]
 name = "ryu"
-version = "1.0.13"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
 
 [[package]]
 name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "serde"
-version = "1.0.163"
+version = "1.0.188"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.163"
+version = "1.0.188"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.37",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.96"
+version = "1.0.107"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
 dependencies = [
  "itoa",
  "ryu",
@@ -314,6 +633,28 @@ dependencies = [
 ]
 
 [[package]]
+name = "serde_with"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
+dependencies = [
+ "serde",
+ "serde_with_macros",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
 name = "special"
 version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -323,10 +664,27 @@ dependencies = [
 ]
 
 [[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
 name = "syn"
-version = "2.0.18"
+version = "2.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -334,13 +692,134 @@ dependencies = [
 ]
 
 [[package]]
+name = "textwrap"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
 name = "unicode-ident"
-version = "1.0.9"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "walkdir"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
 
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.37",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.37",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+
+[[package]]
+name = "web-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/compiler/rustc_codegen_cranelift/patches/regex-lock.toml b/compiler/rustc_codegen_cranelift/patches/regex-lock.toml
index 0e4a33b90ea..e0df6f9ae26 100644
--- a/compiler/rustc_codegen_cranelift/patches/regex-lock.toml
+++ b/compiler/rustc_codegen_cranelift/patches/regex-lock.toml
@@ -4,51 +4,49 @@ version = 3
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.20"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+checksum = "0f2135563fb5c609d2b2b87c1e8ce7bc41b0b45430fa9661f457981503dd5bf0"
 dependencies = [
+ "log",
  "memchr",
 ]
 
 [[package]]
-name = "bitflags"
-version = "1.3.2"
+name = "anyhow"
+version = "1.0.75"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
 
 [[package]]
-name = "bzip2"
-version = "0.3.3"
+name = "arbitrary"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
+checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
 dependencies = [
- "bzip2-sys",
- "libc",
+ "derive_arbitrary",
 ]
 
 [[package]]
-name = "bzip2-sys"
-version = "0.1.11+1.0.8"
+name = "atty"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
- "cc",
+ "hermit-abi",
  "libc",
- "pkg-config",
+ "winapi",
 ]
 
 [[package]]
-name = "cc"
-version = "1.0.79"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
+name = "bstr"
+version = "1.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a"
+dependencies = [
+ "memchr",
+ "serde",
+]
 
 [[package]]
 name = "cfg-if"
@@ -57,114 +55,129 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
-name = "docopt"
-version = "1.1.1"
+name = "derive_arbitrary"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f"
+checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8"
 dependencies = [
- "lazy_static",
- "regex 1.8.3",
- "serde",
- "strsim",
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "filetime"
-version = "0.2.21"
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
+[[package]]
+name = "env_logger"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
+checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
 dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "redox_syscall",
- "windows-sys",
+ "atty",
+ "humantime",
+ "log",
+ "termcolor",
 ]
 
 [[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
 name = "getrandom"
-version = "0.2.9"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
  "wasi",
 ]
 
 [[package]]
-name = "lazy_static"
-version = "1.4.0"
+name = "hashbrown"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
 
 [[package]]
-name = "libc"
-version = "0.2.144"
+name = "hermit-abi"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
-
-[[package]]
-name = "libpcre-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ff3dd28ba96d6fe6752882f2f1b25ba8e1646448e79042442347cf3a92a6666"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 dependencies = [
- "bzip2",
  "libc",
- "pkg-config",
- "tar",
 ]
 
 [[package]]
-name = "memchr"
-version = "2.5.0"
+name = "humantime"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
-name = "memmap"
-version = "0.6.2"
+name = "indexmap"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
 dependencies = [
- "libc",
- "winapi",
+ "equivalent",
+ "hashbrown",
 ]
 
 [[package]]
-name = "onig"
-version = "3.2.2"
+name = "lexopt"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"
+
+[[package]]
+name = "libc"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5eeb268a4620c74ea5768c6d2ccd492d60a47a8754666b91a46bfc35cd4d1ba"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
 dependencies = [
- "bitflags",
- "lazy_static",
- "libc",
- "onig_sys",
+ "log",
 ]
 
 [[package]]
-name = "onig_sys"
-version = "68.2.1"
+name = "memmap2"
+version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "195ebddbb56740be48042ca117b8fb6e0d99fe392191a9362d82f5f69e510379"
+checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
 dependencies = [
- "cc",
  "libc",
- "pkg-config",
 ]
 
 [[package]]
-name = "pkg-config"
-version = "0.3.27"
+name = "once_cell"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.59"
+version = "1.0.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
 dependencies = [
  "unicode-ident",
 ]
@@ -180,9 +193,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.28"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
@@ -206,95 +219,101 @@ dependencies = [
 ]
 
 [[package]]
-name = "redox_syscall"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
 name = "regex"
-version = "1.7.2"
+version = "1.9.5"
 dependencies = [
  "aho-corasick",
- "lazy_static",
+ "anyhow",
+ "doc-comment",
+ "env_logger",
  "memchr",
+ "once_cell",
  "quickcheck",
- "rand",
- "regex-syntax 0.6.29",
+ "regex-automata",
+ "regex-syntax",
+ "regex-test",
 ]
 
 [[package]]
-name = "regex"
-version = "1.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
+name = "regex-automata"
+version = "0.3.8"
 dependencies = [
- "regex-syntax 0.7.2",
+ "aho-corasick",
+ "anyhow",
+ "bstr",
+ "doc-comment",
+ "env_logger",
+ "log",
+ "memchr",
+ "quickcheck",
+ "regex-syntax",
+ "regex-test",
 ]
 
 [[package]]
-name = "regex-benchmark"
+name = "regex-cli"
 version = "0.1.0"
 dependencies = [
- "cc",
- "cfg-if 0.1.10",
- "docopt",
- "lazy_static",
- "libc",
- "libpcre-sys",
- "memmap",
- "onig",
- "pkg-config",
- "regex 1.7.2",
- "regex-syntax 0.6.29",
- "serde",
+ "anyhow",
+ "bstr",
+ "lexopt",
+ "log",
+ "memmap2",
+ "regex",
+ "regex-automata",
+ "regex-lite",
+ "regex-syntax",
+ "tabwriter",
+ "textwrap",
 ]
 
 [[package]]
-name = "regex-debug"
+name = "regex-lite"
 version = "0.1.0"
 dependencies = [
- "docopt",
- "regex 1.7.2",
- "regex-syntax 0.6.29",
- "serde",
+ "anyhow",
+ "regex-test",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.29"
+version = "0.7.5"
+dependencies = [
+ "arbitrary",
+]
 
 [[package]]
-name = "regex-syntax"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+name = "regex-test"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bstr",
+ "serde",
+ "toml",
+]
 
 [[package]]
 name = "rure"
 version = "0.2.2"
 dependencies = [
  "libc",
- "regex 1.7.2",
+ "regex",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.163"
+version = "1.0.188"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.163"
+version = "1.0.188"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -302,16 +321,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "strsim"
-version = "0.10.0"
+name = "serde_spanned"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "syn"
-version = "2.0.18"
+version = "2.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -319,121 +341,117 @@ dependencies = [
 ]
 
 [[package]]
-name = "tar"
-version = "0.4.38"
+name = "tabwriter"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
+checksum = "08e1173ee641651a3095fe95d86ae314cd1f959888097debce3e0f9ca532eef1"
 dependencies = [
- "filetime",
- "libc",
- "xattr",
+ "unicode-width",
 ]
 
 [[package]]
-name = "unicode-ident"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
+name = "termcolor"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
 dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "winapi-util",
 ]
 
 [[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "textwrap"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
 
 [[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "toml"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
 
 [[package]]
-name = "windows-sys"
-version = "0.48.0"
+name = "toml_datetime"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
 dependencies = [
- "windows-targets",
+ "serde",
 ]
 
 [[package]]
-name = "windows-targets"
-version = "0.48.0"
+name = "toml_edit"
+version = "0.19.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
 ]
 
 [[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+name = "unicode-ident"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.0"
+name = "unicode-width"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
 [[package]]
-name = "windows_i686_gnu"
-version = "0.48.0"
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
-name = "windows_i686_msvc"
-version = "0.48.0"
+name = "winapi"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
 
 [[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.0"
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+name = "winapi-util"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
 
 [[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.0"
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "xattr"
-version = "0.2.3"
+name = "winnow"
+version = "0.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
+checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
 dependencies = [
- "libc",
+ "memchr",
 ]
diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
index 5b79d6569bb..de89c4f5eff 100644
--- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
+++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
@@ -174,9 +174,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.146"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 dependencies = [
  "rustc-std-workspace-core",
 ]
@@ -256,6 +256,27 @@ dependencies = [
 ]
 
 [[package]]
+name = "r-efi"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "575fc2d9b3da54adbdfaddf6eca48fec256d977c8630a1750b8991347d1ac911"
+dependencies = [
+ "compiler_builtins",
+ "rustc-std-workspace-core",
+]
+
+[[package]]
+name = "r-efi-alloc"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7"
+dependencies = [
+ "compiler_builtins",
+ "r-efi",
+ "rustc-std-workspace-core",
+]
+
+[[package]]
 name = "rand"
 version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -340,6 +361,7 @@ version = "0.0.0"
 dependencies = [
  "addr2line",
  "alloc",
+ "cc",
  "cfg-if",
  "compiler_builtins",
  "core",
@@ -353,6 +375,8 @@ dependencies = [
  "panic_abort",
  "panic_unwind",
  "profiler_builtins",
+ "r-efi",
+ "r-efi-alloc",
  "rand",
  "rand_xorshift",
  "rustc-demangle",
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index 2cc5d7777a6..86ef127badd 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-09-06"
+channel = "nightly-2023-10-09"
 components = ["rust-src", "rustc-dev", "llvm-tools"]
diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
index f09b9ef12de..60ac6bc9951 100644
--- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -e
 
-./y.sh build --no-unstable-features
+./y.sh build
 
 echo "[SETUP] Rust fork"
 git clone https://github.com/rust-lang/rust.git || true
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index 3fc462a39cc..3b2a12ec028 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -11,22 +11,17 @@ pushd rust
 command -v rg >/dev/null 2>&1 || cargo install ripgrep
 
 rm -r tests/ui/{unsized-locals/,lto/,linkage*} || true
-for test in $(rg --files-with-matches "lto|// needs-asm-support" tests/{codegen-units,ui,incremental}); do
+for test in $(rg --files-with-matches "lto" tests/{codegen-units,ui,incremental}); do
   rm $test
 done
 
-for test in tests/run-make/**/Makefile; do
-  if rg "# needs-asm-support" $test >/dev/null; then
-    rm -r $(dirname $test)
-  fi
-done
-
 for test in $(rg -i --files-with-matches "//(\[\w+\])?~[^\|]*\s*ERR|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" tests/ui); do
   rm $test
 done
 
 git checkout -- tests/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
 git checkout -- tests/ui/proc-macro/pretty-print-hack/
+git checkout -- tests/ui/entry-point/auxiliary/bad_main_functions.rs
 rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
 
 # missing features
@@ -35,8 +30,9 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
 rm -r tests/run-make/comment-section # cg_clif doesn't yet write the .comment section
 
 # requires stack unwinding
-# FIXME add needs-unwind to this test
+# FIXME add needs-unwind to these tests
 rm -r tests/run-make/libtest-junit
+rm tests/ui/asm/may_unwind.rs
 
 # extra warning about -Cpanic=abort for proc macros
 rm tests/ui/proc-macro/crt-static.rs
@@ -77,6 +73,8 @@ rm -r tests/run-make/symbols-include-type-name # --emit=asm not supported
 rm -r tests/run-make/target-specs # i686 not supported by Cranelift
 rm -r tests/run-make/mismatching-target-triples # same
 rm -r tests/run-make/use-extern-for-plugins # same
+rm tests/ui/asm/x86_64/issue-82869.rs # vector regs in inline asm not yet supported
+rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't work entirely correctly
 
 # requires LTO
 rm -r tests/run-make/cdylib
@@ -130,6 +128,7 @@ rm tests/ui/consts/issue-73976-monomorphic.rs # same
 rm tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs # same
 rm tests/ui/consts/const-eval/nonnull_as_ref_ub.rs # same
 rm tests/ui/consts/issue-94675.rs # same
+rm tests/ui/associated-types/issue-85103-layout-debug.rs # same
 
 # rustdoc-clif passes extra args, suppressing the help message when no args are passed
 rm -r tests/run-make/issue-88756-default-output
@@ -154,9 +153,12 @@ rm -r tests/run-make/output-type-permutations # same
 rm -r tests/run-make/used # same
 rm -r tests/run-make/no-alloc-shim
 rm -r tests/run-make/emit-to-stdout
+rm -r tests/run-make/compressed-debuginfo
 
 rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported
 
+rm tests/ui/codegen/subtyping-enforces-type-equality.rs # assert_assignable bug with Generator's
+
 # bugs in the test suite
 # ======================
 rm tests/ui/backtrace.rs # TODO warning
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index 5c7d7b20c5d..c75ad852f82 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -6,6 +6,7 @@ mod returning;
 
 use std::borrow::Cow;
 
+use cranelift_codegen::ir::{AbiParam, SigRef};
 use cranelift_module::ModuleError;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::ty::layout::FnAbiOf;
@@ -13,12 +14,9 @@ use rustc_session::Session;
 use rustc_target::abi::call::{Conv, FnAbi};
 use rustc_target::spec::abi::Abi;
 
-use cranelift_codegen::ir::{AbiParam, SigRef};
-
 use self::pass_mode::*;
-use crate::prelude::*;
-
 pub(crate) use self::returning::codegen_return;
+use crate::prelude::*;
 
 fn clif_sig_from_fn_abi<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
index 0d16da48067..7c9f8c1051c 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
@@ -1,14 +1,14 @@
 //! Argument passing
 
-use crate::prelude::*;
-use crate::value_and_place::assert_assignable;
-
 use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose};
 use rustc_target::abi::call::{
     ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, Reg, RegKind,
 };
 use smallvec::{smallvec, SmallVec};
 
+use crate::prelude::*;
+use crate::value_and_place::assert_assignable;
+
 pub(super) trait ArgAbiExt<'tcx> {
     fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>;
     fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option<AbiParam>, Vec<AbiParam>);
diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs
index 646fb4a3cdc..0799a22c6e1 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs
@@ -1,10 +1,10 @@
 //! Return value handling
 
-use crate::prelude::*;
-
 use rustc_target::abi::call::{ArgAbi, PassMode};
 use smallvec::{smallvec, SmallVec};
 
+use crate::prelude::*;
+
 /// Return a place where the return value of the current function can be written to. If necessary
 /// this adds an extra parameter pointing to where the return value needs to be stored.
 pub(super) fn codegen_return_param<'tcx>(
diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs
index 4e4c595de82..e8af3e8c255 100644
--- a/compiler/rustc_codegen_cranelift/src/allocator.rs
+++ b/compiler/rustc_codegen_cranelift/src/allocator.rs
@@ -1,8 +1,6 @@
 //! Allocator shim
 // Adapted from rustc
 
-use crate::prelude::*;
-
 use rustc_ast::expand::allocator::{
     alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
     ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
@@ -10,6 +8,8 @@ use rustc_ast::expand::allocator::{
 use rustc_codegen_ssa::base::allocator_kind_for_codegen;
 use rustc_session::config::OomStrategy;
 
+use crate::prelude::*;
+
 /// Returns whether an allocator shim was created
 pub(crate) fn codegen(
     tcx: TyCtxt<'_>,
diff --git a/compiler/rustc_codegen_cranelift/src/analyze.rs b/compiler/rustc_codegen_cranelift/src/analyze.rs
index 359d581c153..321612238ea 100644
--- a/compiler/rustc_codegen_cranelift/src/analyze.rs
+++ b/compiler/rustc_codegen_cranelift/src/analyze.rs
@@ -1,11 +1,11 @@
 //! SSA analysis
 
-use crate::prelude::*;
-
 use rustc_index::IndexVec;
 use rustc_middle::mir::StatementKind::*;
 use rustc_middle::ty::Ty;
 
+use crate::prelude::*;
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub(crate) enum SsaKind {
     NotSsa,
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 0c0a1e80a41..ac7389792c8 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -1,15 +1,14 @@
 //! Codegen of a single function
 
+use cranelift_codegen::ir::UserFuncName;
+use cranelift_codegen::CodegenError;
+use cranelift_module::ModuleError;
 use rustc_ast::InlineAsmOptions;
 use rustc_index::IndexVec;
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::layout::FnAbiOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 
-use cranelift_codegen::ir::UserFuncName;
-use cranelift_codegen::CodegenError;
-use cranelift_module::ModuleError;
-
 use crate::constant::ConstantCx;
 use crate::debuginfo::FunctionDebugContext;
 use crate::prelude::*;
diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs
index 6bf3a866ba4..7e027c5f1b3 100644
--- a/compiler/rustc_codegen_cranelift/src/cast.rs
+++ b/compiler/rustc_codegen_cranelift/src/cast.rs
@@ -129,8 +129,8 @@ pub(crate) fn clif_int_or_float_cast(
             let (min, max) = match (to_ty, to_signed) {
                 (types::I8, false) => (0, i64::from(u8::MAX)),
                 (types::I16, false) => (0, i64::from(u16::MAX)),
-                (types::I8, true) => (i64::from(i8::MIN), i64::from(i8::MAX)),
-                (types::I16, true) => (i64::from(i16::MIN), i64::from(i16::MAX)),
+                (types::I8, true) => (i64::from(i8::MIN as u32), i64::from(i8::MAX as u32)),
+                (types::I16, true) => (i64::from(i16::MIN as u32), i64::from(i16::MAX as u32)),
                 _ => unreachable!(),
             };
             let min_val = fx.bcx.ins().iconst(types::I32, min);
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index 359b430b4e5..8958369267e 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -1,6 +1,5 @@
 use cranelift_codegen::isa::TargetFrontendConfig;
 use gimli::write::FileId;
-
 use rustc_data_structures::sync::Lrc;
 use rustc_index::IndexVec;
 use rustc_middle::ty::layout::{
@@ -204,9 +203,9 @@ pub(crate) fn type_min_max_value(
         (types::I8, false) | (types::I16, false) | (types::I32, false) | (types::I64, false) => {
             0i64
         }
-        (types::I8, true) => i64::from(i8::MIN),
-        (types::I16, true) => i64::from(i16::MIN),
-        (types::I32, true) => i64::from(i32::MIN),
+        (types::I8, true) => i64::from(i8::MIN as u8),
+        (types::I16, true) => i64::from(i16::MIN as u16),
+        (types::I32, true) => i64::from(i32::MIN as u32),
         (types::I64, true) => i64::MIN,
         _ => unreachable!(),
     };
@@ -216,9 +215,9 @@ pub(crate) fn type_min_max_value(
         (types::I16, false) => i64::from(u16::MAX),
         (types::I32, false) => i64::from(u32::MAX),
         (types::I64, false) => u64::MAX as i64,
-        (types::I8, true) => i64::from(i8::MAX),
-        (types::I16, true) => i64::from(i16::MAX),
-        (types::I32, true) => i64::from(i32::MAX),
+        (types::I8, true) => i64::from(i8::MAX as u8),
+        (types::I16, true) => i64::from(i16::MAX as u16),
+        (types::I32, true) => i64::from(i32::MAX as u32),
         (types::I64, true) => i64::MAX,
         _ => unreachable!(),
     };
diff --git a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
index d2b928db7d4..20f2ee4c76a 100644
--- a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
+++ b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
@@ -1,8 +1,7 @@
 use std::sync::{Arc, Condvar, Mutex};
 
-use rustc_session::Session;
-
 use jobserver::HelperThread;
+use rustc_session::Session;
 
 // FIXME don't panic when a worker thread panics
 
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 14b10ed8b9a..1cb6fa07723 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -1,12 +1,11 @@
 //! Handling of `static`s, `const`s and promoted allocations
 
+use cranelift_module::*;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::interpret::{read_target_uint, AllocId, GlobalAlloc, Scalar};
 use rustc_middle::mir::ConstValue;
 
-use cranelift_module::*;
-
 use crate::prelude::*;
 
 pub(crate) struct ConstantCx {
@@ -101,7 +100,7 @@ pub(crate) fn codegen_const_value<'tcx>(
                 if fx.clif_type(layout.ty).is_some() {
                     return CValue::const_val(fx, layout, int);
                 } else {
-                    let raw_val = int.to_bits(int.size()).unwrap();
+                    let raw_val = int.size().truncate(int.to_bits(int.size()).unwrap());
                     let val = match int.size().bytes() {
                         1 => fx.bcx.ins().iconst(types::I8, raw_val as i64),
                         2 => fx.bcx.ins().iconst(types::I16, raw_val as i64),
@@ -187,8 +186,7 @@ pub(crate) fn codegen_const_value<'tcx>(
         ConstValue::Slice { data, meta } => {
             let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
             let ptr = pointer_for_allocation(fx, alloc_id).get_addr(fx);
-            // FIXME: the `try_from` here can actually fail, e.g. for very long ZST slices.
-            let len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(meta).unwrap());
+            let len = fx.bcx.ins().iconst(fx.pointer_type, meta as i64);
             CValue::by_val_pair(ptr, len, layout)
         }
     }
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs
index c4a5627e662..81b819a5546 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs
@@ -1,10 +1,9 @@
 //! Write the debuginfo into an object file.
 
 use cranelift_object::ObjectProduct;
-use rustc_data_structures::fx::FxHashMap;
-
 use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer};
 use gimli::{RunTimeEndian, SectionId};
+use rustc_data_structures::fx::FxHashMap;
 
 use super::object::WriteDebugInfo;
 use super::DebugContext;
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index b19b935a0fe..d00d19f9a80 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -3,20 +3,18 @@
 use std::ffi::OsStr;
 use std::path::{Component, Path};
 
-use crate::debuginfo::FunctionDebugContext;
-use crate::prelude::*;
-
-use rustc_data_structures::sync::Lrc;
-use rustc_span::{
-    FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm,
-};
-
 use cranelift_codegen::binemit::CodeOffset;
 use cranelift_codegen::MachSrcLoc;
-
 use gimli::write::{
     Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable,
 };
+use rustc_data_structures::sync::Lrc;
+use rustc_span::{
+    FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm,
+};
+
+use crate::debuginfo::FunctionDebugContext;
+use crate::prelude::*;
 
 // OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
 fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
index 8a4b1cccf14..9e78cc259ce 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
@@ -5,11 +5,8 @@ mod line_info;
 mod object;
 mod unwind;
 
-use crate::prelude::*;
-
 use cranelift_codegen::ir::Endianness;
 use cranelift_codegen::isa::TargetIsa;
-
 use gimli::write::{
     Address, AttributeValue, DwarfUnit, FileId, LineProgram, LineString, Range, RangeList,
     UnitEntryId,
@@ -17,8 +14,9 @@ use gimli::write::{
 use gimli::{Encoding, Format, LineEncoding, RunTimeEndian};
 use indexmap::IndexSet;
 
-pub(crate) use emit::{DebugReloc, DebugRelocName};
-pub(crate) use unwind::UnwindContext;
+pub(crate) use self::emit::{DebugReloc, DebugRelocName};
+pub(crate) use self::unwind::UnwindContext;
+use crate::prelude::*;
 
 pub(crate) fn producer() -> String {
     format!(
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs
index 9dc9b2cf9f8..f1840a7bf73 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs
@@ -1,12 +1,9 @@
-use rustc_data_structures::fx::FxHashMap;
-
 use cranelift_module::FuncId;
 use cranelift_object::ObjectProduct;
-
+use gimli::SectionId;
 use object::write::{Relocation, StandardSegment};
 use object::{RelocationEncoding, SectionKind};
-
-use gimli::SectionId;
+use rustc_data_structures::fx::FxHashMap;
 
 use crate::debuginfo::{DebugReloc, DebugRelocName};
 
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs
index 493359c743f..35278e6fb29 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs
@@ -1,15 +1,13 @@
 //! Unwind info generation (`.eh_frame`)
 
-use crate::prelude::*;
-
 use cranelift_codegen::ir::Endianness;
 use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa};
-
 use cranelift_object::ObjectProduct;
 use gimli::write::{Address, CieId, EhFrame, FrameTable, Section};
 use gimli::RunTimeEndian;
 
 use super::object::WriteDebugInfo;
+use crate::prelude::*;
 
 pub(crate) struct UnwindContext {
     endian: RunTimeEndian,
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index 3e93830951c..49f51f9f956 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -6,19 +6,19 @@ use std::path::PathBuf;
 use std::sync::Arc;
 use std::thread::JoinHandle;
 
+use cranelift_object::{ObjectBuilder, ObjectModule};
+use rustc_codegen_ssa::assert_module_sources::CguReuse;
 use rustc_codegen_ssa::back::metadata::create_compressed_metadata_file;
+use rustc_codegen_ssa::base::determine_cgu_reuse;
 use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
-use rustc_session::cgu_reuse_tracker::CguReuse;
 use rustc_session::config::{DebugInfo, OutputFilenames, OutputType};
 use rustc_session::Session;
 
-use cranelift_object::{ObjectBuilder, ObjectModule};
-
 use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken};
 use crate::global_asm::GlobalAsmConfig;
 use crate::{prelude::*, BackendConfig};
@@ -375,43 +375,47 @@ pub(crate) fn run_aot(
         }
     }
 
+    // Calculate the CGU reuse
+    let cgu_reuse = tcx.sess.time("find_cgu_reuse", || {
+        cgus.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect::<Vec<_>>()
+    });
+
+    rustc_codegen_ssa::assert_module_sources::assert_module_sources(tcx, &|cgu_reuse_tracker| {
+        for (i, cgu) in cgus.iter().enumerate() {
+            let cgu_reuse = cgu_reuse[i];
+            cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
+        }
+    });
+
     let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx));
 
     let mut concurrency_limiter = ConcurrencyLimiter::new(tcx.sess, cgus.len());
 
     let modules = tcx.sess.time("codegen mono items", || {
         cgus.iter()
-            .map(|cgu| {
-                let cgu_reuse = if backend_config.disable_incr_cache {
-                    CguReuse::No
-                } else {
-                    determine_cgu_reuse(tcx, cgu)
-                };
-                tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
-
-                match cgu_reuse {
-                    CguReuse::No => {
-                        let dep_node = cgu.codegen_dep_node(tcx);
-                        tcx.dep_graph
-                            .with_task(
-                                dep_node,
-                                tcx,
-                                (
-                                    backend_config.clone(),
-                                    global_asm_config.clone(),
-                                    cgu.name(),
-                                    concurrency_limiter.acquire(tcx.sess.diagnostic()),
-                                ),
-                                module_codegen,
-                                Some(rustc_middle::dep_graph::hash_result),
-                            )
-                            .0
-                    }
-                    CguReuse::PreLto => unreachable!(),
-                    CguReuse::PostLto => {
-                        concurrency_limiter.job_already_done();
-                        OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))
-                    }
+            .enumerate()
+            .map(|(i, cgu)| match cgu_reuse[i] {
+                CguReuse::No => {
+                    let dep_node = cgu.codegen_dep_node(tcx);
+                    tcx.dep_graph
+                        .with_task(
+                            dep_node,
+                            tcx,
+                            (
+                                backend_config.clone(),
+                                global_asm_config.clone(),
+                                cgu.name(),
+                                concurrency_limiter.acquire(tcx.sess.diagnostic()),
+                            ),
+                            module_codegen,
+                            Some(rustc_middle::dep_graph::hash_result),
+                        )
+                        .0
+                }
+                CguReuse::PreLto => unreachable!("LTO not yet supported"),
+                CguReuse::PostLto => {
+                    concurrency_limiter.job_already_done();
+                    OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))
                 }
             })
             .collect::<Vec<_>>()
@@ -490,32 +494,3 @@ pub(crate) fn run_aot(
         concurrency_limiter,
     })
 }
-
-// Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
-fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
-    if !tcx.dep_graph.is_fully_enabled() {
-        return CguReuse::No;
-    }
-
-    let work_product_id = &cgu.work_product_id();
-    if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
-        // We don't have anything cached for this CGU. This can happen
-        // if the CGU did not exist in the previous session.
-        return CguReuse::No;
-    }
-
-    // Try to mark the CGU as green. If it we can do so, it means that nothing
-    // affecting the LLVM module has changed and we can re-use a cached version.
-    // If we compile with any kind of LTO, this means we can re-use the bitcode
-    // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
-    // know that later). If we are not doing LTO, there is only one optimized
-    // version of each module, so we re-use that.
-    let dep_node = cgu.codegen_dep_node(tcx);
-    assert!(
-        !tcx.dep_graph.dep_node_exists(&dep_node),
-        "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
-        cgu.name()
-    );
-
-    if tcx.try_mark_green(&dep_node) { CguReuse::PostLto } else { CguReuse::No }
-}
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index 1c606494f38..6ee65d12c73 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -6,13 +6,12 @@ use std::ffi::CString;
 use std::os::raw::{c_char, c_int};
 use std::sync::{mpsc, Mutex, OnceLock};
 
+use cranelift_jit::{JITBuilder, JITModule};
 use rustc_codegen_ssa::CrateInfo;
 use rustc_middle::mir::mono::MonoItem;
 use rustc_session::Session;
 use rustc_span::Symbol;
 
-use cranelift_jit::{JITBuilder, JITModule};
-
 use crate::{prelude::*, BackendConfig};
 use crate::{CodegenCx, CodegenMode};
 
diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs
index baadd7a9e81..ebd9b728d90 100644
--- a/compiler/rustc_codegen_cranelift/src/global_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs
@@ -9,16 +9,22 @@ use std::sync::Arc;
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_hir::{InlineAsmOperand, ItemId};
 use rustc_session::config::{OutputFilenames, OutputType};
+use rustc_target::asm::InlineAsmArch;
 
 use crate::prelude::*;
 
 pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, item_id: ItemId) {
     let item = tcx.hir().item(item_id);
     if let rustc_hir::ItemKind::GlobalAsm(asm) = item.kind {
-        if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
-            global_asm.push_str("\n.intel_syntax noprefix\n");
-        } else {
-            global_asm.push_str("\n.att_syntax\n");
+        let is_x86 =
+            matches!(tcx.sess.asm_arch.unwrap(), InlineAsmArch::X86 | InlineAsmArch::X86_64);
+
+        if is_x86 {
+            if !asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
+                global_asm.push_str("\n.intel_syntax noprefix\n");
+            } else {
+                global_asm.push_str("\n.att_syntax\n");
+            }
         }
         for piece in asm.template {
             match *piece {
@@ -65,7 +71,11 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String,
                 }
             }
         }
-        global_asm.push_str("\n.att_syntax\n\n");
+
+        global_asm.push('\n');
+        if is_x86 {
+            global_asm.push_str(".att_syntax\n\n");
+        }
     } else {
         bug!("Expected GlobalAsm found {:?}", item);
     }
@@ -115,11 +125,12 @@ pub(crate) fn compile_global_asm(
     }
 
     // Remove all LLVM style comments
-    let global_asm = global_asm
+    let mut global_asm = global_asm
         .lines()
         .map(|line| if let Some(index) = line.find("//") { &line[0..index] } else { line })
         .collect::<Vec<_>>()
         .join("\n");
+    global_asm.push('\n');
 
     let output_object_file = config.output_filenames.temp_path(OutputType::Object, Some(cgu_name));
 
diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
index 50bbf8105fd..dd2127d554d 100644
--- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
@@ -1,13 +1,14 @@
 //! Codegen of `asm!` invocations.
 
-use crate::prelude::*;
-
 use std::fmt::Write;
 
 use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_middle::mir::InlineAsmOperand;
 use rustc_span::sym;
 use rustc_target::asm::*;
+use target_lexicon::BinaryFormat;
+
+use crate::prelude::*;
 
 enum CInlineAsmOperand<'tcx> {
     In {
@@ -43,7 +44,9 @@ pub(crate) fn codegen_inline_asm<'tcx>(
 ) {
     // FIXME add .eh_frame unwind info directives
 
-    if !template.is_empty() {
+    if !template.is_empty()
+        && (cfg!(not(feature = "inline_asm")) || fx.tcx.sess.target.is_like_windows)
+    {
         // Used by panic_abort
         if template[0] == InlineAsmTemplatePiece::String("int $$0x29".to_string()) {
             fx.bcx.ins().trap(TrapCode::User(1));
@@ -589,11 +592,29 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
     }
 
     fn generate_asm_wrapper(&self, asm_name: &str) -> String {
+        let binary_format = crate::target_triple(self.tcx.sess).binary_format;
+
         let mut generated_asm = String::new();
-        writeln!(generated_asm, ".globl {}", asm_name).unwrap();
-        writeln!(generated_asm, ".type {},@function", asm_name).unwrap();
-        writeln!(generated_asm, ".section .text.{},\"ax\",@progbits", asm_name).unwrap();
-        writeln!(generated_asm, "{}:", asm_name).unwrap();
+        match binary_format {
+            BinaryFormat::Elf => {
+                writeln!(generated_asm, ".globl {}", asm_name).unwrap();
+                writeln!(generated_asm, ".type {},@function", asm_name).unwrap();
+                writeln!(generated_asm, ".section .text.{},\"ax\",@progbits", asm_name).unwrap();
+                writeln!(generated_asm, "{}:", asm_name).unwrap();
+            }
+            BinaryFormat::Macho => {
+                writeln!(generated_asm, ".globl _{}", asm_name).unwrap();
+                writeln!(generated_asm, "_{}:", asm_name).unwrap();
+            }
+            BinaryFormat::Coff => {
+                writeln!(generated_asm, ".globl {}", asm_name).unwrap();
+                writeln!(generated_asm, "{}:", asm_name).unwrap();
+            }
+            _ => self
+                .tcx
+                .sess
+                .fatal(format!("Unsupported binary format for inline asm: {binary_format:?}")),
+        }
 
         let is_x86 = matches!(self.arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
 
@@ -690,8 +711,19 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
         if is_x86 {
             generated_asm.push_str(".att_syntax\n");
         }
-        writeln!(generated_asm, ".size {name}, .-{name}", name = asm_name).unwrap();
-        generated_asm.push_str(".text\n");
+
+        match binary_format {
+            BinaryFormat::Elf => {
+                writeln!(generated_asm, ".size {name}, .-{name}", name = asm_name).unwrap();
+                generated_asm.push_str(".text\n");
+            }
+            BinaryFormat::Macho | BinaryFormat::Coff => {}
+            _ => self
+                .tcx
+                .sess
+                .fatal(format!("Unsupported binary format for inline asm: {binary_format:?}")),
+        }
+
         generated_asm.push_str("\n\n");
 
         generated_asm
@@ -699,25 +731,19 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
 
     fn prologue(generated_asm: &mut String, arch: InlineAsmArch) {
         match arch {
-            InlineAsmArch::X86 => {
-                generated_asm.push_str("    push ebp\n");
-                generated_asm.push_str("    mov ebp,[esp+8]\n");
-            }
             InlineAsmArch::X86_64 => {
                 generated_asm.push_str("    push rbp\n");
-                generated_asm.push_str("    mov rbp,rdi\n");
-            }
-            InlineAsmArch::RiscV32 => {
-                generated_asm.push_str("    addi sp, sp, -8\n");
-                generated_asm.push_str("    sw ra, 4(sp)\n");
-                generated_asm.push_str("    sw s0, 0(sp)\n");
-                generated_asm.push_str("    mv s0, a0\n");
-            }
-            InlineAsmArch::RiscV64 => {
-                generated_asm.push_str("    addi sp, sp, -16\n");
-                generated_asm.push_str("    sd ra, 8(sp)\n");
-                generated_asm.push_str("    sd s0, 0(sp)\n");
-                generated_asm.push_str("    mv s0, a0\n");
+                generated_asm.push_str("    mov rbp,rsp\n");
+                generated_asm.push_str("    push rbx\n"); // rbx is callee saved
+                // rbx is reserved by LLVM for the "base pointer", so rustc doesn't allow using it
+                generated_asm.push_str("    mov rbx,rdi\n");
+            }
+            InlineAsmArch::AArch64 => {
+                generated_asm.push_str("    stp fp, lr, [sp, #-32]!\n");
+                generated_asm.push_str("    mov fp, sp\n");
+                generated_asm.push_str("    str x19, [sp, #24]\n"); // x19 is callee saved
+                // x19 is reserved by LLVM for the "base pointer", so rustc doesn't allow using it
+                generated_asm.push_str("    mov x19, x0\n");
             }
             _ => unimplemented!("prologue for {:?}", arch),
         }
@@ -725,24 +751,14 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
 
     fn epilogue(generated_asm: &mut String, arch: InlineAsmArch) {
         match arch {
-            InlineAsmArch::X86 => {
-                generated_asm.push_str("    pop ebp\n");
-                generated_asm.push_str("    ret\n");
-            }
             InlineAsmArch::X86_64 => {
+                generated_asm.push_str("    pop rbx\n");
                 generated_asm.push_str("    pop rbp\n");
                 generated_asm.push_str("    ret\n");
             }
-            InlineAsmArch::RiscV32 => {
-                generated_asm.push_str("    lw s0, 0(sp)\n");
-                generated_asm.push_str("    lw ra, 4(sp)\n");
-                generated_asm.push_str("    addi sp, sp, 8\n");
-                generated_asm.push_str("    ret\n");
-            }
-            InlineAsmArch::RiscV64 => {
-                generated_asm.push_str("    ld s0, 0(sp)\n");
-                generated_asm.push_str("    ld ra, 8(sp)\n");
-                generated_asm.push_str("    addi sp, sp, 16\n");
+            InlineAsmArch::AArch64 => {
+                generated_asm.push_str("    ldr x19, [sp, #24]\n");
+                generated_asm.push_str("    ldp fp, lr, [sp], #32\n");
                 generated_asm.push_str("    ret\n");
             }
             _ => unimplemented!("epilogue for {:?}", arch),
@@ -751,11 +767,11 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
 
     fn epilogue_noreturn(generated_asm: &mut String, arch: InlineAsmArch) {
         match arch {
-            InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
+            InlineAsmArch::X86_64 => {
                 generated_asm.push_str("    ud2\n");
             }
-            InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
-                generated_asm.push_str("    ebreak\n");
+            InlineAsmArch::AArch64 => {
+                generated_asm.push_str("    brk     #0x1");
             }
             _ => unimplemented!("epilogue_noreturn for {:?}", arch),
         }
@@ -768,25 +784,15 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
         offset: Size,
     ) {
         match arch {
-            InlineAsmArch::X86 => {
-                write!(generated_asm, "    mov [ebp+0x{:x}], ", offset.bytes()).unwrap();
-                reg.emit(generated_asm, InlineAsmArch::X86, None).unwrap();
-                generated_asm.push('\n');
-            }
             InlineAsmArch::X86_64 => {
-                write!(generated_asm, "    mov [rbp+0x{:x}], ", offset.bytes()).unwrap();
+                write!(generated_asm, "    mov [rbx+0x{:x}], ", offset.bytes()).unwrap();
                 reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
                 generated_asm.push('\n');
             }
-            InlineAsmArch::RiscV32 => {
-                generated_asm.push_str("    sw ");
-                reg.emit(generated_asm, InlineAsmArch::RiscV32, None).unwrap();
-                writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
-            }
-            InlineAsmArch::RiscV64 => {
-                generated_asm.push_str("    sd ");
-                reg.emit(generated_asm, InlineAsmArch::RiscV64, None).unwrap();
-                writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
+            InlineAsmArch::AArch64 => {
+                generated_asm.push_str("    str ");
+                reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap();
+                writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap();
             }
             _ => unimplemented!("save_register for {:?}", arch),
         }
@@ -799,25 +805,15 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
         offset: Size,
     ) {
         match arch {
-            InlineAsmArch::X86 => {
-                generated_asm.push_str("    mov ");
-                reg.emit(generated_asm, InlineAsmArch::X86, None).unwrap();
-                writeln!(generated_asm, ", [ebp+0x{:x}]", offset.bytes()).unwrap();
-            }
             InlineAsmArch::X86_64 => {
                 generated_asm.push_str("    mov ");
                 reg.emit(generated_asm, InlineAsmArch::X86_64, None).unwrap();
-                writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap();
-            }
-            InlineAsmArch::RiscV32 => {
-                generated_asm.push_str("    lw ");
-                reg.emit(generated_asm, InlineAsmArch::RiscV32, None).unwrap();
-                writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
+                writeln!(generated_asm, ", [rbx+0x{:x}]", offset.bytes()).unwrap();
             }
-            InlineAsmArch::RiscV64 => {
-                generated_asm.push_str("    ld ");
-                reg.emit(generated_asm, InlineAsmArch::RiscV64, None).unwrap();
-                writeln!(generated_asm, ", 0x{:x}(s0)", offset.bytes()).unwrap();
+            InlineAsmArch::AArch64 => {
+                generated_asm.push_str("    ldr ");
+                reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap();
+                writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap();
             }
             _ => unimplemented!("restore_register for {:?}", arch),
         }
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
index 63b5402f2b6..c1694760998 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
@@ -1,10 +1,10 @@
 //! Emulate LLVM intrinsics
 
+use rustc_middle::ty::GenericArgsRef;
+
 use crate::intrinsics::*;
 use crate::prelude::*;
 
-use rustc_middle::ty::GenericArgsRef;
-
 pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     intrinsic: &str,
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs
index c20a9915930..0c211a06dc4 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs
@@ -1,10 +1,10 @@
 //! Emulate AArch64 LLVM intrinsics
 
+use rustc_middle::ty::GenericArgsRef;
+
 use crate::intrinsics::*;
 use crate::prelude::*;
 
-use rustc_middle::ty::GenericArgsRef;
-
 pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     intrinsic: &str,
@@ -156,6 +156,41 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>(
             });
         }
 
+        // FIXME generalize vector types
+        "llvm.aarch64.neon.tbl1.v16i8" => {
+            intrinsic_args!(fx, args => (t, idx); intrinsic);
+
+            let zero = fx.bcx.ins().iconst(types::I8, 0);
+            for i in 0..16 {
+                let idx_lane = idx.value_lane(fx, i).load_scalar(fx);
+                let is_zero =
+                    fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThanOrEqual, idx_lane, 16);
+                let t_idx = fx.bcx.ins().uextend(fx.pointer_type, idx_lane);
+                let t_lane = t.value_lane_dyn(fx, t_idx).load_scalar(fx);
+                let res = fx.bcx.ins().select(is_zero, zero, t_lane);
+                ret.place_lane(fx, i).to_ptr().store(fx, res, MemFlags::trusted());
+            }
+        }
+
+        // FIXME generalize vector types
+        "llvm.aarch64.neon.umaxp.v16i8" => {
+            intrinsic_args!(fx, args => (a, b); intrinsic);
+
+            // FIXME add helper for horizontal pairwise operations
+            for i in 0..8 {
+                let lane1 = a.value_lane(fx, i * 2).load_scalar(fx);
+                let lane2 = a.value_lane(fx, i * 2 + 1).load_scalar(fx);
+                let res = fx.bcx.ins().umax(lane1, lane2);
+                ret.place_lane(fx, i).to_ptr().store(fx, res, MemFlags::trusted());
+            }
+            for i in 0..8 {
+                let lane1 = b.value_lane(fx, i * 2).load_scalar(fx);
+                let lane2 = b.value_lane(fx, i * 2 + 1).load_scalar(fx);
+                let res = fx.bcx.ins().umax(lane1, lane2);
+                ret.place_lane(fx, 8 + i).to_ptr().store(fx, res, MemFlags::trusted());
+            }
+        }
+
         /*
         _ if intrinsic.starts_with("llvm.aarch64.neon.sshl.v")
             || intrinsic.starts_with("llvm.aarch64.neon.sqshl.v")
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
index e62de6b6147..0c9a94e1c23 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
@@ -1,10 +1,10 @@
 //! Emulate x86 LLVM intrinsics
 
+use rustc_middle::ty::GenericArgsRef;
+
 use crate::intrinsics::*;
 use crate::prelude::*;
 
-use rustc_middle::ty::GenericArgsRef;
-
 pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     intrinsic: &str,
@@ -74,8 +74,10 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             };
             let x = codegen_operand(fx, x);
             let y = codegen_operand(fx, y);
-            let kind = crate::constant::mir_operand_get_const_val(fx, kind)
-                .expect("llvm.x86.sse2.cmp.* kind not const");
+            let kind = match kind {
+                Operand::Constant(const_) => crate::constant::eval_mir_constant(fx, const_).0,
+                Operand::Copy(_) | Operand::Move(_) => unreachable!("{kind:?}"),
+            };
 
             let flt_cc = match kind
                 .try_to_bits(Size::from_bytes(1))
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 36e9ba9c7f8..e94091e6a25 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -18,17 +18,16 @@ mod llvm_aarch64;
 mod llvm_x86;
 mod simd;
 
-pub(crate) use cpuid::codegen_cpuid_call;
-pub(crate) use llvm::codegen_llvm_intrinsic_call;
-
+use cranelift_codegen::ir::AtomicRmwOp;
 use rustc_middle::ty;
 use rustc_middle::ty::layout::{HasParamEnv, ValidityRequirement};
 use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_span::symbol::{kw, sym, Symbol};
 
+pub(crate) use self::cpuid::codegen_cpuid_call;
+pub(crate) use self::llvm::codegen_llvm_intrinsic_call;
 use crate::prelude::*;
-use cranelift_codegen::ir::AtomicRmwOp;
 
 fn bug_on_incorrect_arg_count(intrinsic: impl std::fmt::Display) -> ! {
     bug!("wrong number of args for intrinsic {}", intrinsic);
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index 6efbe149863..ea137c4ca1e 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -148,7 +148,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
             let total_len = lane_count * 2;
 
             let indexes =
-                idx.iter().map(|idx| idx.unwrap_leaf().try_to_u16().unwrap()).collect::<Vec<u16>>();
+                idx.iter().map(|idx| idx.unwrap_leaf().try_to_u32().unwrap()).collect::<Vec<u32>>();
 
             for &idx in &indexes {
                 assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
@@ -216,8 +216,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
 
             let indexes = {
                 use rustc_middle::mir::interpret::*;
-                let idx_const = crate::constant::mir_operand_get_const_val(fx, idx)
-                    .expect("simd_shuffle idx not const");
+                let idx_const = match idx {
+                    Operand::Constant(const_) => crate::constant::eval_mir_constant(fx, const_).0,
+                    Operand::Copy(_) | Operand::Move(_) => unreachable!("{idx:?}"),
+                };
 
                 let idx_bytes = match idx_const {
                     ConstValue::Indirect { alloc_id, offset } => {
@@ -343,7 +345,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, ret_lane);
         }
 
-        sym::simd_neg => {
+        sym::simd_neg
+        | sym::simd_bswap
+        | sym::simd_bitreverse
+        | sym::simd_ctlz
+        | sym::simd_cttz => {
             intrinsic_args!(fx, args => (a); intrinsic);
 
             if !a.layout().ty.is_simd() {
@@ -351,16 +357,21 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 return;
             }
 
-            simd_for_each_lane(
-                fx,
-                a,
-                ret,
-                &|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() {
-                    ty::Int(_) => fx.bcx.ins().ineg(lane),
-                    ty::Float(_) => fx.bcx.ins().fneg(lane),
-                    _ => unreachable!(),
-                },
-            );
+            simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| match (
+                lane_ty.kind(),
+                intrinsic,
+            ) {
+                (ty::Int(_), sym::simd_neg) => fx.bcx.ins().ineg(lane),
+                (ty::Float(_), sym::simd_neg) => fx.bcx.ins().fneg(lane),
+
+                (ty::Uint(ty::UintTy::U8) | ty::Int(ty::IntTy::I8), sym::simd_bswap) => lane,
+                (ty::Uint(_) | ty::Int(_), sym::simd_bswap) => fx.bcx.ins().bswap(lane),
+                (ty::Uint(_) | ty::Int(_), sym::simd_bitreverse) => fx.bcx.ins().bitrev(lane),
+                (ty::Uint(_) | ty::Int(_), sym::simd_ctlz) => fx.bcx.ins().clz(lane),
+                (ty::Uint(_) | ty::Int(_), sym::simd_cttz) => fx.bcx.ins().ctz(lane),
+
+                _ => unreachable!(),
+            });
         }
 
         sym::simd_add
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index d01ded8abaa..522fe7e425b 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -29,6 +29,8 @@ use std::any::Any;
 use std::cell::{Cell, RefCell};
 use std::sync::Arc;
 
+use cranelift_codegen::isa::TargetIsa;
+use cranelift_codegen::settings::{self, Configurable};
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_codegen_ssa::CodegenResults;
 use rustc_data_structures::profiling::SelfProfilerRef;
@@ -39,9 +41,6 @@ use rustc_session::config::OutputFilenames;
 use rustc_session::Session;
 use rustc_span::Symbol;
 
-use cranelift_codegen::isa::TargetIsa;
-use cranelift_codegen::settings::{self, Configurable};
-
 pub use crate::config::*;
 use crate::prelude::*;
 
@@ -76,22 +75,6 @@ mod value_and_place;
 mod vtable;
 
 mod prelude {
-    pub(crate) use rustc_span::{FileNameDisplayPreference, Span};
-
-    pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-    pub(crate) use rustc_middle::bug;
-    pub(crate) use rustc_middle::mir::{self, *};
-    pub(crate) use rustc_middle::ty::layout::{self, LayoutOf, TyAndLayout};
-    pub(crate) use rustc_middle::ty::{
-        self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, TypeAndMut,
-        TypeFoldable, TypeVisitableExt, UintTy,
-    };
-    pub(crate) use rustc_target::abi::{Abi, FieldIdx, Scalar, Size, VariantIdx, FIRST_VARIANT};
-
-    pub(crate) use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
-
-    pub(crate) use rustc_index::Idx;
-
     pub(crate) use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
     pub(crate) use cranelift_codegen::ir::function::Function;
     pub(crate) use cranelift_codegen::ir::types;
@@ -103,6 +86,18 @@ mod prelude {
     pub(crate) use cranelift_codegen::Context;
     pub(crate) use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
     pub(crate) use cranelift_module::{self, DataDescription, FuncId, Linkage, Module};
+    pub(crate) use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+    pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+    pub(crate) use rustc_index::Idx;
+    pub(crate) use rustc_middle::bug;
+    pub(crate) use rustc_middle::mir::{self, *};
+    pub(crate) use rustc_middle::ty::layout::{self, LayoutOf, TyAndLayout};
+    pub(crate) use rustc_middle::ty::{
+        self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, TypeAndMut,
+        TypeFoldable, TypeVisitableExt, UintTy,
+    };
+    pub(crate) use rustc_span::{FileNameDisplayPreference, Span};
+    pub(crate) use rustc_target::abi::{Abi, FieldIdx, Scalar, Size, VariantIdx, FIRST_VARIANT};
 
     pub(crate) use crate::abi::*;
     pub(crate) use crate::base::{codegen_operand, codegen_place};
@@ -263,9 +258,9 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn isa::Tar
     let preserve_frame_pointer = sess.target.options.frame_pointer
         != rustc_target::spec::FramePointer::MayOmit
         || matches!(sess.opts.cg.force_frame_pointers, Some(true));
-    if preserve_frame_pointer {
-        flags_builder.set("preserve_frame_pointers", "true").unwrap();
-    }
+    flags_builder
+        .set("preserve_frame_pointers", if preserve_frame_pointer { "true" } else { "false" })
+        .unwrap();
 
     let tls_model = match target_triple.binary_format {
         BinaryFormat::Elf => "elf_gd",
diff --git a/compiler/rustc_codegen_cranelift/src/pointer.rs b/compiler/rustc_codegen_cranelift/src/pointer.rs
index b60e56720ed..11ac6b94678 100644
--- a/compiler/rustc_codegen_cranelift/src/pointer.rs
+++ b/compiler/rustc_codegen_cranelift/src/pointer.rs
@@ -1,11 +1,10 @@
 //! Defines [`Pointer`] which is used to improve the quality of the generated clif ir for pointer
 //! operations.
 
-use crate::prelude::*;
-
+use cranelift_codegen::ir::immediates::Offset32;
 use rustc_target::abi::Align;
 
-use cranelift_codegen::ir::immediates::Offset32;
+use crate::prelude::*;
 
 /// A pointer pointing either to a certain address, a certain stack slot or nothing.
 #[derive(Copy, Clone, Debug)]
diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
index 0ead50c34ea..da84e54a916 100644
--- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
+++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
@@ -63,8 +63,8 @@ use cranelift_codegen::{
     ir::entities::AnyEntity,
     write::{FuncWriter, PlainWriter},
 };
-
 use rustc_middle::ty::layout::FnAbiOf;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_session::config::{OutputFilenames, OutputType};
 
 use crate::prelude::*;
@@ -80,15 +80,17 @@ impl CommentWriter {
     pub(crate) fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
         let enabled = should_write_ir(tcx);
         let global_comments = if enabled {
-            vec![
-                format!("symbol {}", tcx.symbol_name(instance).name),
-                format!("instance {:?}", instance),
-                format!(
-                    "abi {:?}",
-                    RevealAllLayoutCx(tcx).fn_abi_of_instance(instance, ty::List::empty())
-                ),
-                String::new(),
-            ]
+            with_no_trimmed_paths!({
+                vec![
+                    format!("symbol {}", tcx.symbol_name(instance).name),
+                    format!("instance {:?}", instance),
+                    format!(
+                        "abi {:?}",
+                        RevealAllLayoutCx(tcx).fn_abi_of_instance(instance, ty::List::empty())
+                    ),
+                    String::new(),
+                ]
+            })
         } else {
             vec![]
         };
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index 45893a4f3ac..3a6a6c9e3f5 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -1,11 +1,10 @@
 //! Definition of [`CValue`] and [`CPlace`]
 
-use crate::prelude::*;
-
-use rustc_middle::ty::FnSig;
-
 use cranelift_codegen::entity::EntityRef;
 use cranelift_codegen::ir::immediates::Offset32;
+use rustc_middle::ty::FnSig;
+
+use crate::prelude::*;
 
 fn codegen_field<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
@@ -310,7 +309,8 @@ impl<'tcx> CValue<'tcx> {
                 fx.bcx.ins().iconcat(lsb, msb)
             }
             ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Ref(..) | ty::RawPtr(..) => {
-                fx.bcx.ins().iconst(clif_ty, const_val.to_bits(layout.size).unwrap() as i64)
+                let raw_val = const_val.size().truncate(const_val.to_bits(layout.size).unwrap());
+                fx.bcx.ins().iconst(clif_ty, raw_val as i64)
             }
             ty::Float(FloatTy::F32) => {
                 fx.bcx.ins().f32const(Ieee32::with_bits(u32::try_from(const_val).unwrap()))
diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml
index d2b7724a221..f075c744e45 100644
--- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml
+++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml
@@ -57,8 +57,8 @@ jobs:
       uses: dawidd6/action-download-artifact@v2
       with:
           workflow: main.yml
-          name: ${{ matrix.libgccjit_version.gcc }}
-          path: gcc-build
+          name: gcc-13
+          path: gcc-13
           repo: antoyo/gcc
           branch: ${{ matrix.libgccjit_version.artifacts_branch }}
           event: push
@@ -71,9 +71,8 @@ jobs:
     - name: Setup path to libgccjit
       if: matrix.libgccjit_version.gcc != 'libgccjit12.so'
       run: |
-          echo $(readlink -f gcc-build) > gcc_path
-          # NOTE: the filename is still libgccjit.so even when the artifact name is different.
-          ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0
+          sudo dpkg --force-overwrite -i gcc-13/gcc-13.deb
+          echo /usr/lib/ > gcc_path
 
     - name: Set env
       run: |
@@ -119,8 +118,8 @@ jobs:
 
     - name: Build
       run: |
-        ./prepare_build.sh
-        ${{ matrix.libgccjit_version.env_extra }} ./build.sh ${{ matrix.libgccjit_version.extra }}
+        ./y.sh prepare --only-libcore
+        ${{ matrix.libgccjit_version.env_extra }} ./y.sh build ${{ matrix.libgccjit_version.extra }}
         ${{ matrix.libgccjit_version.env_extra }} cargo test ${{ matrix.libgccjit_version.extra }}
         ./clean_all.sh
 
@@ -128,7 +127,7 @@ jobs:
       run: |
         git config --global user.email "user@example.com"
         git config --global user.name "User"
-        ./prepare.sh
+        ./y.sh prepare
 
     # Compile is a separate step, as the actions-rs/cargo action supports error annotations
     - name: Compile
@@ -141,6 +140,9 @@ jobs:
       if: ${{ matrix.libgccjit_version.gcc == 'libgccjit12.so' }}
       run: cat failing-ui-tests12.txt >> failing-ui-tests.txt
 
+    - name: Add more failing tests because the sysroot is not compiled with LTO
+      run: cat failing-non-lto-tests.txt >> failing-ui-tests.txt
+
     - name: Run tests
       run: |
         ${{ matrix.libgccjit_version.env_extra }} ./test.sh --release --clean --build-sysroot ${{ matrix.commands }} ${{ matrix.libgccjit_version.extra }}
diff --git a/compiler/rustc_codegen_gcc/.github/workflows/release.yml b/compiler/rustc_codegen_gcc/.github/workflows/release.yml
index c4e99469bc2..bd0415040e7 100644
--- a/compiler/rustc_codegen_gcc/.github/workflows/release.yml
+++ b/compiler/rustc_codegen_gcc/.github/workflows/release.yml
@@ -18,8 +18,6 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        libgccjit_version:
-          - { gcc: "libgccjit.so", artifacts_branch: "master" }
         commands: [
           "--test-successful-rustc --nb-parts 2 --current-part 0",
           "--test-successful-rustc --nb-parts 2 --current-part 1",
@@ -40,18 +38,17 @@ jobs:
       uses: dawidd6/action-download-artifact@v2
       with:
           workflow: main.yml
-          name: ${{ matrix.libgccjit_version.gcc }}
-          path: gcc-build
+          name: gcc-13
+          path: gcc-13
           repo: antoyo/gcc
-          branch: ${{ matrix.libgccjit_version.artifacts_branch }}
+          branch: "master"
           event: push
           search_artifacts: true # Because, instead, the action only check the last job ran and that won't work since we want multiple artifacts.
 
     - name: Setup path to libgccjit
       run: |
-          echo $(readlink -f gcc-build) > gcc_path
-          # NOTE: the filename is still libgccjit.so even when the artifact name is different.
-          ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0
+          sudo dpkg --force-overwrite -i gcc-13/gcc-13.deb
+          echo /usr/lib/ > gcc_path
 
     - name: Set env
       run: |
@@ -88,8 +85,8 @@ jobs:
 
     - name: Build
       run: |
-        ./prepare_build.sh
-        ./build.sh --release --release-sysroot
+        ./y.sh prepare --only-libcore
+        EMBED_LTO_BITCODE=1 ./y.sh build --release --release-sysroot
         cargo test
         ./clean_all.sh
 
@@ -97,7 +94,9 @@ jobs:
       run: |
         git config --global user.email "user@example.com"
         git config --global user.name "User"
-        ./prepare.sh
+        ./y.sh prepare
+        # FIXME(antoyo): we cannot enable LTO for stdarch tests currently because of some failing LTO tests using proc-macros.
+        echo -n 'lto = "fat"' >> build_sysroot/Cargo.toml
 
     # Compile is a separate step, as the actions-rs/cargo action supports error annotations
     - name: Compile
@@ -106,6 +105,9 @@ jobs:
         command: build
         args: --release
 
+    - name: Add more failing tests because of undefined symbol errors (FIXME)
+      run: cat failing-lto-tests.txt >> failing-ui-tests.txt
+
     - name: Run tests
       run: |
-        ./test.sh --release --clean --release-sysroot --build-sysroot ${{ matrix.commands }}
+        EMBED_LTO_BITCODE=1 ./test.sh --release --clean --release-sysroot --build-sysroot ${{ matrix.commands }}
diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml
index 556c6444833..6c28326823c 100644
--- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml
+++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml
@@ -18,8 +18,6 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        libgccjit_version:
-          - { gcc: "libgccjit.so", artifacts_branch: "master" }
         cargo_runner: [
           "sde -future -rtm_mode full --",
           "",
@@ -54,18 +52,17 @@ jobs:
       uses: dawidd6/action-download-artifact@v2
       with:
           workflow: main.yml
-          name: ${{ matrix.libgccjit_version.gcc }}
-          path: gcc-build
+          name: gcc-13
+          path: gcc-13
           repo: antoyo/gcc
-          branch: ${{ matrix.libgccjit_version.artifacts_branch }}
+          branch: "master"
           event: push
           search_artifacts: true # Because, instead, the action only check the last job ran and that won't work since we want multiple artifacts.
 
     - name: Setup path to libgccjit
       run: |
-          echo $(readlink -f gcc-build) > gcc_path
-          # NOTE: the filename is still libgccjit.so even when the artifact name is different.
-          ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0
+          sudo dpkg --force-overwrite -i gcc-13/gcc-13.deb
+          echo /usr/lib/ > gcc_path
 
     - name: Set env
       run: |
@@ -102,8 +99,8 @@ jobs:
 
     - name: Build
       run: |
-        ./prepare_build.sh
-        ./build.sh --release --release-sysroot
+        ./y.sh prepare --only-libcore
+        ./y.sh build --release --release-sysroot
         cargo test
 
     - name: Clean
@@ -115,7 +112,7 @@ jobs:
       run: |
         git config --global user.email "user@example.com"
         git config --global user.name "User"
-        ./prepare.sh
+        ./y.sh prepare
 
     # Compile is a separate step, as the actions-rs/cargo action supports error annotations
     - name: Compile
@@ -133,10 +130,11 @@ jobs:
       if: ${{ !matrix.cargo_runner }}
       run: |
         cd build_sysroot/sysroot_src/library/stdarch/
-        CHANNEL=release TARGET=x86_64-unknown-linux-gnu ../../../../cargo.sh test
+        CHANNEL=release TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ../../../../cargo.sh test
 
     - name: Run stdarch tests
       if: ${{ matrix.cargo_runner }}
       run: |
         cd build_sysroot/sysroot_src/library/stdarch/
-        STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu ../../../../cargo.sh test -- --skip rtm --skip tbm --skip sse4a
+        # FIXME: these tests fail when the sysroot is compiled with LTO because of a missing symbol in proc-macro.
+        STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features" ../../../../cargo.sh test -- --skip rtm --skip tbm --skip sse4a
diff --git a/compiler/rustc_codegen_gcc/.gitignore b/compiler/rustc_codegen_gcc/.gitignore
index c5ed7de200c..b44d1aa78c2 100644
--- a/compiler/rustc_codegen_gcc/.gitignore
+++ b/compiler/rustc_codegen_gcc/.gitignore
@@ -25,3 +25,4 @@ tools/llvmint
 tools/llvmint-2
 # The `llvm` folder is generated by the `tools/generate_intrinsics.py` script to update intrinsics.
 llvm
+build_system/target
diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock
index 1c8754bf675..85675fc40c3 100644
--- a/compiler/rustc_codegen_gcc/Cargo.lock
+++ b/compiler/rustc_codegen_gcc/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.18"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
 dependencies = [
  "memchr",
 ]
@@ -18,12 +18,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+
+[[package]]
 name = "fm"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -35,7 +74,7 @@ dependencies = [
 [[package]]
 name = "gccjit"
 version = "1.0.0"
-source = "git+https://github.com/antoyo/gccjit.rs#d6e52626cfc6f487094a5d5ac66302baf3439984"
+source = "git+https://github.com/antoyo/gccjit.rs#0b158c68bf7e46732869d90550a98e886dee8858"
 dependencies = [
  "gccjit_sys",
 ]
@@ -43,7 +82,7 @@ dependencies = [
 [[package]]
 name = "gccjit_sys"
 version = "0.0.1"
-source = "git+https://github.com/antoyo/gccjit.rs#d6e52626cfc6f487094a5d5ac66302baf3439984"
+source = "git+https://github.com/antoyo/gccjit.rs#0b158c68bf7e46732869d90550a98e886dee8858"
 dependencies = [
  "libc",
 ]
@@ -58,24 +97,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "getrandom"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
 
 [[package]]
 name = "lang_tester"
@@ -95,86 +120,55 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.112"
+version = "0.2.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
 
 [[package]]
-name = "memchr"
-version = "2.4.1"
+name = "linux-raw-sys"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
 
 [[package]]
-name = "num_cpus"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.15"
+name = "memchr"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
-name = "rand"
-version = "0.8.4"
+name = "num_cpus"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
+ "hermit-abi",
  "libc",
- "rand_chacha",
- "rand_core",
- "rand_hc",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
-dependencies = [
- "getrandom",
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.1"
+name = "object"
+version = "0.30.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
 dependencies = [
- "rand_core",
+ "memchr",
 ]
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.10"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -183,18 +177,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
 
 [[package]]
 name = "rustc_codegen_gcc"
@@ -202,11 +187,25 @@ version = "0.1.0"
 dependencies = [
  "gccjit",
  "lang_tester",
+ "object",
  "smallvec",
  "tempfile",
 ]
 
 [[package]]
+name = "rustix"
+version = "0.38.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
+dependencies = [
+ "bitflags 2.4.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
 name = "same-file"
 version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -223,23 +222,22 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
 
 [[package]]
 name = "tempfile"
-version = "3.2.0"
+version = "3.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
 dependencies = [
  "cfg-if",
- "libc",
- "rand",
+ "fastrand",
  "redox_syscall",
- "remove_dir_all",
- "winapi",
+ "rustix",
+ "windows-sys",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.2"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
 dependencies = [
  "winapi-util",
 ]
@@ -255,9 +253,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-width"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
 [[package]]
 name = "wait-timeout"
@@ -270,22 +268,15 @@ dependencies = [
 
 [[package]]
 name = "walkdir"
-version = "2.3.2"
+version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
 dependencies = [
  "same-file",
- "winapi",
  "winapi-util",
 ]
 
 [[package]]
-name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
-
-[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -315,3 +306,69 @@ name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml
index 81066d9ce1f..51fab147aa2 100644
--- a/compiler/rustc_codegen_gcc/Cargo.toml
+++ b/compiler/rustc_codegen_gcc/Cargo.toml
@@ -27,7 +27,13 @@ gccjit = { git = "https://github.com/antoyo/gccjit.rs" }
 # Local copy.
 #gccjit = { path = "../gccjit.rs" }
 
+object = { version = "0.30.1", default-features = false, features = [
+    "std",
+    "read",
+] }
 smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
+# TODO(antoyo): make tempfile optional.
+tempfile = "3.7.1"
 
 [dev-dependencies]
 lang_tester = "0.3.9"
diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md
index a93637d9038..f001c83b08d 100644
--- a/compiler/rustc_codegen_gcc/Readme.md
+++ b/compiler/rustc_codegen_gcc/Readme.md
@@ -1,6 +1,7 @@
 # WIP libgccjit codegen backend for rust
 
 [![Chat on IRC](https://img.shields.io/badge/irc.libera.chat-%23rustc__codegen__gcc-blue.svg)](https://web.libera.chat/#rustc_codegen_gcc)
+[![Chat on Matrix](https://img.shields.io/badge/matrix.org-%23rustc__codegen__gcc-blue.svg)](https://matrix.to/#/#rustc_codegen_gcc:matrix.org)
 
 This is a GCC codegen for rustc, which means it can be loaded by the existing rustc frontend, but benefits from GCC: more architectures are supported and GCC's optimizations are used.
 
@@ -14,9 +15,7 @@ A secondary goal is to check if using the gcc backend will provide any run-time
 ## Building
 
 **This requires a patched libgccjit in order to work.
-The patches in [this repository](https://github.com/antoyo/libgccjit-patches) need to be applied.
-(Those patches should work when applied on master, but in case it doesn't work, they are known to work when applied on 079c23cfe079f203d5df83fea8e92a60c7d7e878.)
-You can also use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.**
+You need to use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.**
 
 To build it (most of these instructions come from [here](https://gcc.gnu.org/onlinedocs/jit/internals/index.html), so don't hesitate to take a look there if you encounter an issue):
 
@@ -66,8 +65,8 @@ $ export RUST_COMPILER_RT_ROOT="$PWD/llvm/compiler-rt"
 Then you can run commands like this:
 
 ```bash
-$ ./prepare.sh # download and patch sysroot src and install hyperfine for benchmarking
-$ LIBRARY_PATH=$(cat gcc_path) LD_LIBRARY_PATH=$(cat gcc_path) ./build.sh --release
+$ ./y.sh prepare # download and patch sysroot src and install hyperfine for benchmarking
+$ LIBRARY_PATH=$(cat gcc_path) LD_LIBRARY_PATH=$(cat gcc_path) ./y.sh build --release
 ```
 
 To run the tests:
@@ -78,22 +77,29 @@ $ ./test.sh --release
 
 ## Usage
 
-`$cg_gccjit_dir` is the directory you cloned this repo into in the following instructions.
+`$CG_GCCJIT_DIR` is the directory you cloned this repo into in the following instructions:
+
+```bash
+export CG_GCCJIT_DIR=[the full path to rustc_codegen_gcc]
+```
 
 ### Cargo
 
 ```bash
-$ CHANNEL="release" $cg_gccjit_dir/cargo.sh run
+$ CHANNEL="release" $CG_GCCJIT_DIR/cargo.sh run
 ```
 
 If you compiled cg_gccjit in debug mode (aka you didn't pass `--release` to `./test.sh`) you should use `CHANNEL="debug"` instead or omit `CHANNEL="release"` completely.
 
+To use LTO, you need to set the variable `FAT_LTO=1` and `EMBED_LTO_BITCODE=1` in addition to setting `lto = "fat"` in the `Cargo.toml`.
+Don't set `FAT_LTO` when compiling the sysroot, though: only set `EMBED_LTO_BITCODE=1`.
+
 ### Rustc
 
 > You should prefer using the Cargo method.
 
 ```bash
-$ rustc +$(cat $cg_gccjit_dir/rust-toolchain) -Cpanic=abort -Zcodegen-backend=$cg_gccjit_dir/target/release/librustc_codegen_gcc.so --sysroot $cg_gccjit_dir/build_sysroot/sysroot my_crate.rs
+$ LIBRARY_PATH=$(cat gcc_path) LD_LIBRARY_PATH=$(cat gcc_path) rustc +$(cat $CG_GCCJIT_DIR/rust-toolchain | grep 'channel' | cut -d '=' -f 2 | sed 's/"//g' | sed 's/ //g') -Cpanic=abort -Zcodegen-backend=$CG_GCCJIT_DIR/target/release/librustc_codegen_gcc.so --sysroot $CG_GCCJIT_DIR/build_sysroot/sysroot my_crate.rs
 ```
 
 ## Env vars
@@ -105,8 +111,18 @@ $ rustc +$(cat $cg_gccjit_dir/rust-toolchain) -Cpanic=abort -Zcodegen-backend=$c
     object files when their content should have been changed by a change to cg_gccjit.</dd>
     <dt>CG_GCCJIT_DISPLAY_CG_TIME</dt>
     <dd>Display the time it took to perform codegen for a crate</dd>
+    <dt>CG_RUSTFLAGS</dt>
+    <dd>Send additional flags to rustc. Can be used to build the sysroot without unwinding by setting `CG_RUSTFLAGS=-Cpanic=abort`.</dd>
+    <dt>CG_GCCJIT_DUMP_TO_FILE</dt>
+    <dd>Dump a C-like representation to /tmp/gccjit_dumps and enable debug info in order to debug this C-like representation.</dd>
 </dl>
 
+## Licensing
+
+While this crate is licensed under a dual Apache/MIT license, it links to `libgccjit` which is under the GPLv3+ and thus, the resulting toolchain (rustc + GCC codegen) will need to be released under the GPL license.
+
+However, programs compiled with `rustc_codegen_gcc` do not need to be released under a GPL license.
+
 ## Debugging
 
 Sometimes, libgccjit will crash and output an error like this:
@@ -182,6 +198,61 @@ set substitute-path /usr/src/debug/gcc /path/to/gcc-repo/gcc
 
 TODO(antoyo): but that's not what I remember I was doing.
 
+### `failed to build archive` error
+
+When you get this error:
+
+```
+error: failed to build archive: failed to open object file: No such file or directory (os error 2)
+```
+
+That can be caused by the fact that you try to compile with `lto = "fat"`, but you didn't compile the sysroot with LTO.
+(Not sure if that's the reason since I cannot reproduce anymore. Maybe it happened when forgetting setting `FAT_LTO`.)
+
+### ld: cannot find crtbegin.o
+
+When compiling an executable with libgccijt, if setting the `*LIBRARY_PATH` variables to the install directory, you will get the following errors:
+
+```
+ld: cannot find crtbegin.o: No such file or directory
+ld: cannot find -lgcc: No such file or directory
+ld: cannot find -lgcc: No such file or directory
+libgccjit.so: error: error invoking gcc driver
+```
+
+To fix this, set the variables to `gcc-build/build/gcc`.
+
+### How to debug GCC LTO
+
+Run do the command with `-v -save-temps` and then extract the `lto1` line from the output and run that under the debugger.
+
+### How to send arguments to the GCC linker
+
+```
+CG_RUSTFLAGS="-Clink-args=-save-temps -v" ../cargo.sh build
+```
+
+### How to see the personality functions in the asm dump
+
+```
+CG_RUSTFLAGS="-Clink-arg=-save-temps -v -Clink-arg=-dA" ../cargo.sh build
+```
+
+### How to see the LLVM IR for a sysroot crate
+
+```
+cargo build -v --target x86_64-unknown-linux-gnu -Zbuild-std
+# Take the command from the output and add --emit=llvm-ir
+```
+
+### To prevent the linker from unmangling symbols
+
+Run with:
+
+```
+COLLECT_NO_DEMANGLE=1
+```
+
 ### How to use a custom-build rustc
 
  * Build the stage2 compiler (`rustup toolchain link debug-current build/x86_64-unknown-linux-gnu/stage2`).
@@ -223,6 +294,11 @@ https://rust-lang.zulipchat.com/#narrow/stream/301329-t-devtools/topic/subtree.2
 
 `rustc` needs to be built without `jemalloc` so that `mem-trace` can overload `malloc` since `jemalloc` is linked statically, so a `LD_PRELOAD`-ed library won't a chance to intercept the calls to `malloc`.
 
+### How to generate GIMPLE
+
+If you need to check what gccjit is generating (GIMPLE), then take a look at how to
+generate it in [gimple.md](./doc/gimple.md).
+
 ### How to build a cross-compiling libgccjit
 
 #### Building libgccjit
@@ -239,4 +315,4 @@ https://rust-lang.zulipchat.com/#narrow/stream/301329-t-devtools/topic/subtree.2
  * Set `linker='-Clinker=m68k-linux-gcc'`.
  * Set the path to the cross-compiling libgccjit in `gcc_path`.
  * Comment the line: `context.add_command_line_option("-masm=intel");` in src/base.rs.
- * (might not be necessary) Disable the compilation of libstd.so (and possibly libcore.so?).
+ * (might not be necessary) Disable the compilation of libstd.so (and possibly libcore.so?): Remove dylib from build_sysroot/sysroot_src/library/std/Cargo.toml.
diff --git a/compiler/rustc_codegen_gcc/build.sh b/compiler/rustc_codegen_gcc/build.sh
deleted file mode 100755
index ba0d0d04948..00000000000
--- a/compiler/rustc_codegen_gcc/build.sh
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env bash
-
-#set -x
-set -e
-
-codegen_channel=debug
-sysroot_channel=debug
-
-flags=
-
-while [[ $# -gt 0 ]]; do
-    case $1 in
-        --release)
-            codegen_channel=release
-            shift
-            ;;
-        --release-sysroot)
-            sysroot_channel=release
-            shift
-            ;;
-        --no-default-features)
-            flags="$flags --no-default-features"
-            shift
-            ;;
-        --features)
-            shift
-            flags="$flags --features $1"
-            shift
-            ;;
-        *)
-            echo "Unknown option $1"
-            exit 1
-            ;;
-    esac
-done
-
-if [ -f ./gcc_path ]; then
-    export GCC_PATH=$(cat gcc_path)
-else
-    echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
-    exit 1
-fi
-
-export LD_LIBRARY_PATH="$GCC_PATH"
-export LIBRARY_PATH="$GCC_PATH"
-
-if [[ "$codegen_channel" == "release" ]]; then
-    export CHANNEL='release'
-    CARGO_INCREMENTAL=1 cargo rustc --release $flags
-else
-    echo $LD_LIBRARY_PATH
-    export CHANNEL='debug'
-    cargo rustc $flags
-fi
-
-source config.sh
-
-rm -r target/out || true
-mkdir -p target/out/gccjit
-
-echo "[BUILD] sysroot"
-if [[ "$sysroot_channel" == "release" ]]; then
-    time ./build_sysroot/build_sysroot.sh --release
-else
-    time ./build_sysroot/build_sysroot.sh
-fi
-
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml b/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml
index a84f86a8218..e5658273c97 100644
--- a/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml
+++ b/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml
@@ -2,6 +2,7 @@
 authors = ["bjorn3 <bjorn3@users.noreply.github.com>"]
 name = "sysroot"
 version = "0.0.0"
+resolver = "2"
 
 [dependencies]
 core = { path = "./sysroot_src/library/core" }
@@ -18,3 +19,4 @@ rustc-std-workspace-std = { path = "./sysroot_src/library/rustc-std-workspace-st
 
 [profile.release]
 debug = true
+#lto = "fat" # TODO(antoyo): re-enable when the failing LTO tests regarding proc-macros are fixed.
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh b/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh
index 9d692d599f6..851e9895ce2 100755
--- a/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh
+++ b/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh
@@ -5,9 +5,9 @@
 set -e
 cd $(dirname "$0")
 
-pushd ../ >/dev/null
+pushd ../
 source ./config.sh
-popd >/dev/null
+popd
 
 # Cleanup for previous run
 #     v Clean target dir except for build scripts and incremental cache
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh b/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh
deleted file mode 100755
index 71b3876bac2..00000000000
--- a/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env bash
-set -e
-cd $(dirname "$0")
-
-SRC_DIR=$(dirname $(rustup which rustc))"/../lib/rustlib/src/rust/"
-DST_DIR="sysroot_src"
-
-if [ ! -e $SRC_DIR ]; then
-    echo "Please install rust-src component"
-    exit 1
-fi
-
-rm -rf $DST_DIR
-mkdir -p $DST_DIR/library
-cp -r $SRC_DIR/library $DST_DIR/
-
-pushd $DST_DIR
-echo "[GIT] init"
-git init
-echo "[GIT] add"
-git add .
-echo "[GIT] commit"
-
-# This is needed on systems where nothing is configured.
-# git really needs something here, or it will fail.
-# Even using --author is not enough.
-git config user.email || git config user.email "none@example.com"
-git config user.name || git config user.name "None"
-
-git commit -m "Initial commit" -q
-for file in $(ls ../../patches/ | grep -v patcha); do
-    echo "[GIT] apply" $file
-    git apply ../../patches/$file
-    git add -A
-    git commit --no-gpg-sign -m "Patch $file"
-done
-popd
-
-echo "Successfully prepared libcore for building"
diff --git a/compiler/rustc_codegen_gcc/build_system/Cargo.lock b/compiler/rustc_codegen_gcc/build_system/Cargo.lock
new file mode 100644
index 00000000000..86268e19160
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "y"
+version = "0.1.0"
diff --git a/compiler/rustc_codegen_gcc/build_system/Cargo.toml b/compiler/rustc_codegen_gcc/build_system/Cargo.toml
new file mode 100644
index 00000000000..f36709ea036
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "y"
+version = "0.1.0"
+edition = "2021"
+
+[[bin]]
+name = "y"
+path = "src/main.rs"
diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs
new file mode 100644
index 00000000000..e2819c37ad9
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs
@@ -0,0 +1,233 @@
+use crate::config::set_config;
+use crate::utils::{
+    get_gcc_path, run_command, run_command_with_env, run_command_with_output_and_env, walk_dir,
+};
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::fs;
+use std::path::Path;
+
+#[derive(Default)]
+struct BuildArg {
+    codegen_release_channel: bool,
+    sysroot_release_channel: bool,
+    features: Vec<String>,
+    gcc_path: String,
+}
+
+impl BuildArg {
+    fn new() -> Result<Option<Self>, String> {
+        let gcc_path = get_gcc_path()?;
+        let mut build_arg = Self {
+            gcc_path,
+            ..Default::default()
+        };
+        // We skip binary name and the `build` command.
+        let mut args = std::env::args().skip(2);
+
+        while let Some(arg) = args.next() {
+            match arg.as_str() {
+                "--release" => build_arg.codegen_release_channel = true,
+                "--release-sysroot" => build_arg.sysroot_release_channel = true,
+                "--no-default-features" => {
+                    build_arg.features.push("--no-default-features".to_string());
+                }
+                "--features" => {
+                    if let Some(arg) = args.next() {
+                        build_arg.features.push("--features".to_string());
+                        build_arg.features.push(arg.as_str().into());
+                    } else {
+                        return Err(
+                            "Expected a value after `--features`, found nothing".to_string()
+                        );
+                    }
+                }
+                "--help" => {
+                    Self::usage();
+                    return Ok(None);
+                }
+                arg => return Err(format!("Unknown argument `{}`", arg)),
+            }
+        }
+        Ok(Some(build_arg))
+    }
+
+    fn usage() {
+        println!(
+            r#"
+`build` command help:
+
+    --release              : Build codegen in release mode
+    --release-sysroot      : Build sysroot in release mode
+    --no-default-features  : Add `--no-default-features` flag
+    --features [arg]       : Add a new feature [arg]
+    --help                 : Show this help
+"#
+        )
+    }
+}
+
+fn build_sysroot(
+    env: &mut HashMap<String, String>,
+    release_mode: bool,
+    target_triple: &str,
+) -> Result<(), String> {
+    std::env::set_current_dir("build_sysroot")
+        .map_err(|error| format!("Failed to go to `build_sysroot` directory: {:?}", error))?;
+    // Cleanup for previous run
+    // Clean target dir except for build scripts and incremental cache
+    let _ = walk_dir(
+        "target",
+        |dir: &Path| {
+            for top in &["debug", "release"] {
+                let _ = fs::remove_dir_all(dir.join(top).join("build"));
+                let _ = fs::remove_dir_all(dir.join(top).join("deps"));
+                let _ = fs::remove_dir_all(dir.join(top).join("examples"));
+                let _ = fs::remove_dir_all(dir.join(top).join("native"));
+
+                let _ = walk_dir(
+                    dir.join(top),
+                    |sub_dir: &Path| {
+                        if sub_dir
+                            .file_name()
+                            .map(|filename| filename.to_str().unwrap().starts_with("libsysroot"))
+                            .unwrap_or(false)
+                        {
+                            let _ = fs::remove_dir_all(sub_dir);
+                        }
+                        Ok(())
+                    },
+                    |file: &Path| {
+                        if file
+                            .file_name()
+                            .map(|filename| filename.to_str().unwrap().starts_with("libsysroot"))
+                            .unwrap_or(false)
+                        {
+                            let _ = fs::remove_file(file);
+                        }
+                        Ok(())
+                    },
+                );
+            }
+            Ok(())
+        },
+        |_| Ok(()),
+    );
+
+    let _ = fs::remove_file("Cargo.lock");
+    let _ = fs::remove_file("test_target/Cargo.lock");
+    let _ = fs::remove_dir_all("sysroot");
+
+    // Builds libs
+    let channel = if release_mode {
+        let rustflags = env
+            .get("RUSTFLAGS")
+            .cloned()
+            .unwrap_or_default();
+        env.insert(
+            "RUSTFLAGS".to_string(),
+            format!("{} -Zmir-opt-level=3", rustflags),
+        );
+        run_command_with_output_and_env(
+            &[
+                &"cargo",
+                &"build",
+                &"--target",
+                &target_triple,
+                &"--release",
+            ],
+            None,
+            Some(&env),
+        )?;
+        "release"
+    } else {
+        run_command_with_output_and_env(
+            &[
+                &"cargo",
+                &"build",
+                &"--target",
+                &target_triple,
+                &"--features",
+                &"compiler_builtins/c",
+            ],
+            None,
+            Some(env),
+        )?;
+        "debug"
+    };
+
+    // Copy files to sysroot
+    let sysroot_path = format!("sysroot/lib/rustlib/{}/lib/", target_triple);
+    fs::create_dir_all(&sysroot_path)
+        .map_err(|error| format!("Failed to create directory `{}`: {:?}", sysroot_path, error))?;
+    let copier = |dir_to_copy: &Path| {
+        run_command(&[&"cp", &"-r", &dir_to_copy, &sysroot_path], None).map(|_| ())
+    };
+    walk_dir(
+        &format!("target/{}/{}/deps", target_triple, channel),
+        copier,
+        copier,
+    )?;
+
+    Ok(())
+}
+
+fn build_codegen(args: &BuildArg) -> Result<(), String> {
+    let mut env = HashMap::new();
+
+    let current_dir =
+        std::env::current_dir().map_err(|error| format!("`current_dir` failed: {:?}", error))?;
+    if let Ok(rt_root) = std::env::var("RUST_COMPILER_RT_ROOT") {
+        env.insert("RUST_COMPILER_RT_ROOT".to_string(), rt_root);
+    } else {
+        env.insert(
+            "RUST_COMPILER_RT_ROOT".to_string(),
+            format!("{}", current_dir.join("llvm/compiler-rt").display()),
+        );
+    }
+    env.insert("LD_LIBRARY_PATH".to_string(), args.gcc_path.clone());
+    env.insert("LIBRARY_PATH".to_string(), args.gcc_path.clone());
+
+    let mut command: Vec<&dyn AsRef<OsStr>> = vec![&"cargo", &"rustc"];
+    if args.codegen_release_channel {
+        command.push(&"--release");
+        env.insert("CHANNEL".to_string(), "release".to_string());
+        env.insert("CARGO_INCREMENTAL".to_string(), "1".to_string());
+    } else {
+        env.insert("CHANNEL".to_string(), "debug".to_string());
+    }
+    let ref_features = args.features.iter().map(|s| s.as_str()).collect::<Vec<_>>();
+    for feature in &ref_features {
+        command.push(feature);
+    }
+    run_command_with_env(&command, None, Some(&env))?;
+
+    let config = set_config(&mut env, &[], Some(&args.gcc_path))?;
+
+    // We voluntarily ignore the error.
+    let _ = fs::remove_dir_all("target/out");
+    let gccjit_target = "target/out/gccjit";
+    fs::create_dir_all(gccjit_target).map_err(|error| {
+        format!(
+            "Failed to create directory `{}`: {:?}",
+            gccjit_target, error
+        )
+    })?;
+
+    println!("[BUILD] sysroot");
+    build_sysroot(
+        &mut env,
+        args.sysroot_release_channel,
+        &config.target_triple,
+    )?;
+    Ok(())
+}
+
+pub fn run() -> Result<(), String> {
+    let args = match BuildArg::new()? {
+        Some(args) => args,
+        None => return Ok(()),
+    };
+    build_codegen(&args)?;
+    Ok(())
+}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/config.rs b/compiler/rustc_codegen_gcc/build_system/src/config.rs
new file mode 100644
index 00000000000..4f2e33f0f99
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/config.rs
@@ -0,0 +1,125 @@
+use crate::utils::{get_gcc_path, get_os_name, get_rustc_host_triple};
+use std::collections::HashMap;
+use std::env as std_env;
+
+pub struct ConfigInfo {
+    pub target_triple: String,
+    pub rustc_command: Vec<String>,
+    pub run_wrapper: Option<&'static str>,
+}
+
+// Returns the beginning for the command line of rustc.
+pub fn set_config(
+    env: &mut HashMap<String, String>,
+    test_flags: &[String],
+    gcc_path: Option<&str>,
+) -> Result<ConfigInfo, String> {
+    env.insert("CARGO_INCREMENTAL".to_string(), "0".to_string());
+
+    let gcc_path = match gcc_path {
+        Some(path) => path.to_string(),
+        None => get_gcc_path()?,
+    };
+    env.insert("GCC_PATH".to_string(), gcc_path.clone());
+
+    let os_name = get_os_name()?;
+    let dylib_ext = match os_name.as_str() {
+        "Linux" => "so",
+        "Darwin" => "dylib",
+        os => return Err(format!("unsupported OS `{}`", os)),
+    };
+    let host_triple = get_rustc_host_triple()?;
+    let mut linker = None;
+    let mut target_triple = host_triple.as_str();
+    let mut run_wrapper = None;
+    // FIXME: handle this with a command line flag?
+    // let mut target_triple = "m68k-unknown-linux-gnu";
+
+    if host_triple != target_triple {
+        if target_triple == "m68k-unknown-linux-gnu" {
+            target_triple = "mips-unknown-linux-gnu";
+            linker = Some("-Clinker=m68k-linux-gcc");
+        } else if target_triple == "aarch64-unknown-linux-gnu" {
+            // We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
+            linker = Some("-Clinker=aarch64-linux-gnu-gcc");
+            run_wrapper = Some("qemu-aarch64 -L /usr/aarch64-linux-gnu");
+        } else {
+            return Err(format!("unknown non-native platform `{}`", target_triple));
+        }
+    }
+    let current_dir =
+        std_env::current_dir().map_err(|error| format!("`current_dir` failed: {:?}", error))?;
+    let channel = if let Some(channel) = env.get("CHANNEL") {
+        channel.as_str()
+    } else {
+        "debug"
+    };
+    let cg_backend_path = current_dir
+        .join("target")
+        .join(channel)
+        .join(&format!("librustc_codegen_gcc.{}", dylib_ext));
+    let sysroot_path = current_dir.join("build_sysroot/sysroot");
+    let mut rustflags = Vec::new();
+    if let Some(cg_rustflags) = env.get("CG_RUSTFLAGS") {
+        rustflags.push(cg_rustflags.clone());
+    }
+    if let Some(linker) = linker {
+        rustflags.push(linker.to_string());
+    }
+    rustflags.extend_from_slice(&[
+        "-Csymbol-mangling-version=v0".to_string(),
+        "-Cdebuginfo=2".to_string(),
+        format!("-Zcodegen-backend={}", cg_backend_path.display()),
+        "--sysroot".to_string(),
+        sysroot_path.display().to_string(),
+    ]);
+
+    // Since we don't support ThinLTO, disable LTO completely when not trying to do LTO.
+    // TODO(antoyo): remove when we can handle ThinLTO.
+    if !env.contains_key(&"FAT_LTO".to_string()) {
+        rustflags.push("-Clto=off".to_string());
+    }
+    rustflags.extend_from_slice(test_flags);
+    // FIXME(antoyo): remove once the atomic shim is gone
+    if os_name == "Darwin" {
+        rustflags.extend_from_slice(&[
+            "-Clink-arg=-undefined".to_string(),
+            "-Clink-arg=dynamic_lookup".to_string(),
+        ]);
+    }
+    env.insert("RUSTFLAGS".to_string(), rustflags.join(" "));
+    // display metadata load errors
+    env.insert("RUSTC_LOG".to_string(), "warn".to_string());
+
+    let sysroot = current_dir.join(&format!(
+        "build_sysroot/sysroot/lib/rustlib/{}/lib",
+        target_triple
+    ));
+    let ld_library_path = format!(
+        "{target}:{sysroot}:{gcc_path}",
+        target = current_dir.join("target/out").display(),
+        sysroot = sysroot.display(),
+    );
+    env.insert("LD_LIBRARY_PATH".to_string(), ld_library_path.clone());
+    env.insert("DYLD_LIBRARY_PATH".to_string(), ld_library_path);
+
+    // NOTE: To avoid the -fno-inline errors, use /opt/gcc/bin/gcc instead of cc.
+    // To do so, add a symlink for cc to /opt/gcc/bin/gcc in our PATH.
+    // Another option would be to add the following Rust flag: -Clinker=/opt/gcc/bin/gcc
+    let path = std::env::var("PATH").unwrap_or_default();
+    env.insert("PATH".to_string(), format!("/opt/gcc/bin:{}", path));
+
+    let mut rustc_command = vec!["rustc".to_string()];
+    rustc_command.extend_from_slice(&rustflags);
+    rustc_command.extend_from_slice(&[
+        "-L".to_string(),
+        "crate=target/out".to_string(),
+        "--out-dir".to_string(),
+        "target/out".to_string(),
+    ]);
+    Ok(ConfigInfo {
+        target_triple: target_triple.to_string(),
+        rustc_command,
+        run_wrapper,
+    })
+}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/main.rs b/compiler/rustc_codegen_gcc/build_system/src/main.rs
new file mode 100644
index 00000000000..332a14ff0a2
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/main.rs
@@ -0,0 +1,62 @@
+use std::env;
+use std::process;
+
+mod build;
+mod config;
+mod prepare;
+mod rustc_info;
+mod utils;
+
+macro_rules! arg_error {
+    ($($err:tt)*) => {{
+        eprintln!($($err)*);
+        eprintln!();
+        usage();
+        std::process::exit(1);
+    }};
+}
+
+fn usage() {
+    println!(
+        "\
+Available commands for build_system:
+
+    prepare  : Run prepare command
+    build    : Run build command
+    --help   : Show this message"
+    );
+}
+
+pub enum Command {
+    Prepare,
+    Build,
+}
+
+fn main() {
+    if env::var("RUST_BACKTRACE").is_err() {
+        env::set_var("RUST_BACKTRACE", "1");
+    }
+
+    let command = match env::args().nth(1).as_deref() {
+        Some("prepare") => Command::Prepare,
+        Some("build") => Command::Build,
+        Some("--help") => {
+            usage();
+            process::exit(0);
+        }
+        Some(flag) if flag.starts_with('-') => arg_error!("Expected command found flag {}", flag),
+        Some(command) => arg_error!("Unknown command {}", command),
+        None => {
+            usage();
+            process::exit(0);
+        }
+    };
+
+    if let Err(e) = match command {
+        Command::Prepare => prepare::run(),
+        Command::Build => build::run(),
+    } {
+        eprintln!("Command failed to run: {e:?}");
+        process::exit(1);
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/prepare.rs b/compiler/rustc_codegen_gcc/build_system/src/prepare.rs
new file mode 100644
index 00000000000..b258ddf3664
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/prepare.rs
@@ -0,0 +1,227 @@
+use crate::rustc_info::get_rustc_path;
+use crate::utils::{cargo_install, git_clone, run_command, run_command_with_output, walk_dir};
+
+use std::fs;
+use std::path::Path;
+
+fn prepare_libcore(sysroot_path: &Path) -> Result<(), String> {
+    let rustc_path = match get_rustc_path() {
+        Some(path) => path,
+        None => return Err("`rustc` path not found".to_string()),
+    };
+
+    let parent = match rustc_path.parent() {
+        Some(path) => path,
+        None => return Err(format!("No parent for `{}`", rustc_path.display())),
+    };
+
+    let rustlib_dir = parent
+        .join("../lib/rustlib/src/rust")
+        .canonicalize()
+        .map_err(|error| format!("Failed to canonicalize path: {:?}", error))?;
+    if !rustlib_dir.is_dir() {
+        return Err("Please install `rust-src` component".to_string());
+    }
+
+    let sysroot_dir = sysroot_path.join("sysroot_src");
+    if sysroot_dir.is_dir() {
+        if let Err(error) = fs::remove_dir_all(&sysroot_dir) {
+            return Err(format!(
+                "Failed to remove `{}`: {:?}",
+                sysroot_dir.display(),
+                error,
+            ));
+        }
+    }
+
+    let sysroot_library_dir = sysroot_dir.join("library");
+    fs::create_dir_all(&sysroot_library_dir).map_err(|error| {
+        format!(
+            "Failed to create folder `{}`: {:?}",
+            sysroot_library_dir.display(),
+            error,
+        )
+    })?;
+
+    run_command(
+        &[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir],
+        None,
+    )?;
+
+    println!("[GIT] init (cwd): `{}`", sysroot_dir.display());
+    run_command(&[&"git", &"init"], Some(&sysroot_dir))?;
+    println!("[GIT] add (cwd): `{}`", sysroot_dir.display());
+    run_command(&[&"git", &"add", &"."], Some(&sysroot_dir))?;
+    println!("[GIT] commit (cwd): `{}`", sysroot_dir.display());
+
+    // This is needed on systems where nothing is configured.
+    // git really needs something here, or it will fail.
+    // Even using --author is not enough.
+    run_command(
+        &[&"git", &"config", &"user.email", &"none@example.com"],
+        Some(&sysroot_dir),
+    )?;
+    run_command(
+        &[&"git", &"config", &"user.name", &"None"],
+        Some(&sysroot_dir),
+    )?;
+    run_command(
+        &[&"git", &"config", &"core.autocrlf", &"false"],
+        Some(&sysroot_dir),
+    )?;
+    run_command(
+        &[&"git", &"config", &"commit.gpgSign", &"false"],
+        Some(&sysroot_dir),
+    )?;
+    run_command(
+        &[&"git", &"commit", &"-m", &"Initial commit", &"-q"],
+        Some(&sysroot_dir),
+    )?;
+
+    let mut patches = Vec::new();
+    walk_dir(
+        "patches",
+        |_| Ok(()),
+        |file_path: &Path| {
+            patches.push(file_path.to_path_buf());
+            Ok(())
+        },
+    )?;
+    patches.sort();
+    for file_path in patches {
+        println!("[GIT] apply `{}`", file_path.display());
+        let path = Path::new("../..").join(file_path);
+        run_command_with_output(&[&"git", &"apply", &path], Some(&sysroot_dir))?;
+        run_command_with_output(&[&"git", &"add", &"-A"], Some(&sysroot_dir))?;
+        run_command_with_output(
+            &[
+                &"git",
+                &"commit",
+                &"--no-gpg-sign",
+                &"-m",
+                &format!("Patch {}", path.display()),
+            ],
+            Some(&sysroot_dir),
+        )?;
+    }
+    println!("Successfully prepared libcore for building");
+    Ok(())
+}
+
+// build with cg_llvm for perf comparison
+fn build_raytracer(repo_dir: &Path) -> Result<(), String> {
+    run_command(&[&"cargo", &"build"], Some(repo_dir))?;
+    let mv_target = repo_dir.join("raytracer_cg_llvm");
+    if mv_target.is_file() {
+        std::fs::remove_file(&mv_target)
+            .map_err(|e| format!("Failed to remove file `{}`: {e:?}", mv_target.display()))?;
+    }
+    run_command(
+        &[&"mv", &"target/debug/main", &"raytracer_cg_llvm"],
+        Some(repo_dir),
+    )?;
+    Ok(())
+}
+
+fn clone_and_setup<F>(repo_url: &str, checkout_commit: &str, extra: Option<F>) -> Result<(), String>
+where
+    F: Fn(&Path) -> Result<(), String>,
+{
+    let clone_result = git_clone(repo_url, None)?;
+    if !clone_result.ran_clone {
+        println!("`{}` has already been cloned", clone_result.repo_name);
+    }
+    let repo_path = Path::new(&clone_result.repo_name);
+    run_command(&[&"git", &"checkout", &"--", &"."], Some(&repo_path))?;
+    run_command(&[&"git", &"checkout", &checkout_commit], Some(&repo_path))?;
+    let filter = format!("-{}-", clone_result.repo_name);
+    walk_dir(
+        "crate_patches",
+        |_| Ok(()),
+        |file_path| {
+            let patch = file_path.as_os_str().to_str().unwrap();
+            if patch.contains(&filter) && patch.ends_with(".patch") {
+                run_command_with_output(
+                    &[&"git", &"am", &file_path.canonicalize().unwrap()],
+                    Some(&repo_path),
+                )?;
+            }
+            Ok(())
+        },
+    )?;
+    if let Some(extra) = extra {
+        extra(&repo_path)?;
+    }
+    Ok(())
+}
+
+struct PrepareArg {
+    only_libcore: bool,
+}
+
+impl PrepareArg {
+    fn new() -> Result<Option<Self>, String> {
+        let mut only_libcore = false;
+
+        for arg in std::env::args().skip(2) {
+            match arg.as_str() {
+                "--only-libcore" => only_libcore = true,
+                "--help" => {
+                    Self::usage();
+                    return Ok(None);
+                }
+                a => return Err(format!("Unknown argument `{a}`")),
+            }
+        }
+        Ok(Some(Self { only_libcore }))
+    }
+
+    fn usage() {
+        println!(
+            r#"
+`prepare` command help:
+
+    --only-libcore  : Only setup libcore and don't clone other repositories
+    --help          : Show this help
+"#
+        )
+    }
+}
+
+pub fn run() -> Result<(), String> {
+    let args = match PrepareArg::new()? {
+        Some(a) => a,
+        None => return Ok(()),
+    };
+    let sysroot_path = Path::new("build_sysroot");
+    prepare_libcore(sysroot_path)?;
+
+    if !args.only_libcore {
+        cargo_install("hyperfine")?;
+
+        let to_clone = &[
+            (
+                "https://github.com/rust-random/rand.git",
+                "0f933f9c7176e53b2a3c7952ded484e1783f0bf1",
+                None,
+            ),
+            (
+                "https://github.com/rust-lang/regex.git",
+                "341f207c1071f7290e3f228c710817c280c8dca1",
+                None,
+            ),
+            (
+                "https://github.com/ebobby/simple-raytracer",
+                "804a7a21b9e673a482797aa289a18ed480e4d813",
+                Some(build_raytracer),
+            ),
+        ];
+
+        for (repo_url, checkout_commit, cb) in to_clone {
+            clone_and_setup(repo_url, checkout_commit, *cb)?;
+        }
+    }
+
+    println!("Successfully ran `prepare`");
+    Ok(())
+}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/rustc_info.rs b/compiler/rustc_codegen_gcc/build_system/src/rustc_info.rs
new file mode 100644
index 00000000000..0988b56d81e
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/rustc_info.rs
@@ -0,0 +1,12 @@
+use std::path::{Path, PathBuf};
+
+use crate::utils::run_command;
+
+pub fn get_rustc_path() -> Option<PathBuf> {
+    if let Ok(rustc) = std::env::var("RUSTC") {
+        return Some(PathBuf::from(rustc));
+    }
+    run_command(&[&"rustup", &"which", &"rustc"], None)
+        .ok()
+        .map(|out| Path::new(String::from_utf8(out.stdout).unwrap().trim()).to_path_buf())
+}
diff --git a/compiler/rustc_codegen_gcc/build_system/src/utils.rs b/compiler/rustc_codegen_gcc/build_system/src/utils.rs
new file mode 100644
index 00000000000..536f33a8029
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/build_system/src/utils.rs
@@ -0,0 +1,240 @@
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::fmt::Debug;
+use std::fs;
+use std::path::Path;
+use std::process::{Command, ExitStatus, Output};
+
+fn get_command_inner(
+    input: &[&dyn AsRef<OsStr>],
+    cwd: Option<&Path>,
+    env: Option<&HashMap<String, String>>,
+) -> Command {
+    let (cmd, args) = match input {
+        [] => panic!("empty command"),
+        [cmd, args @ ..] => (cmd, args),
+    };
+    let mut command = Command::new(cmd);
+    command.args(args);
+    if let Some(cwd) = cwd {
+        command.current_dir(cwd);
+    }
+    if let Some(env) = env {
+        command.envs(env.iter().map(|(k, v)| (k.as_str(), v.as_str())));
+    }
+    command
+}
+
+fn check_exit_status(
+    input: &[&dyn AsRef<OsStr>],
+    cwd: Option<&Path>,
+    exit_status: ExitStatus,
+) -> Result<(), String> {
+    if exit_status.success() {
+        Ok(())
+    } else {
+        Err(format!(
+            "Command `{}`{} exited with status {:?}",
+            input
+                .iter()
+                .map(|s| s.as_ref().to_str().unwrap())
+                .collect::<Vec<_>>()
+                .join(" "),
+            cwd.map(|cwd| format!(" (running in folder `{}`)", cwd.display()))
+                .unwrap_or_default(),
+            exit_status.code(),
+        ))
+    }
+}
+
+fn command_error<D: Debug>(input: &[&dyn AsRef<OsStr>], cwd: &Option<&Path>, error: D) -> String {
+    format!(
+        "Command `{}`{} failed to run: {error:?}",
+        input
+            .iter()
+            .map(|s| s.as_ref().to_str().unwrap())
+            .collect::<Vec<_>>()
+            .join(" "),
+        cwd.as_ref()
+            .map(|cwd| format!(" (running in folder `{}`)", cwd.display(),))
+            .unwrap_or_default(),
+    )
+}
+
+pub fn run_command(input: &[&dyn AsRef<OsStr>], cwd: Option<&Path>) -> Result<Output, String> {
+    run_command_with_env(input, cwd, None)
+}
+
+pub fn run_command_with_env(
+    input: &[&dyn AsRef<OsStr>],
+    cwd: Option<&Path>,
+    env: Option<&HashMap<String, String>>,
+) -> Result<Output, String> {
+    let output = get_command_inner(input, cwd, env)
+        .output()
+        .map_err(|e| command_error(input, &cwd, e))?;
+    check_exit_status(input, cwd, output.status)?;
+    Ok(output)
+}
+
+pub fn run_command_with_output(
+    input: &[&dyn AsRef<OsStr>],
+    cwd: Option<&Path>,
+) -> Result<(), String> {
+    let exit_status = get_command_inner(input, cwd, None)
+        .spawn()
+        .map_err(|e| command_error(input, &cwd, e))?
+        .wait()
+        .map_err(|e| command_error(input, &cwd, e))?;
+    check_exit_status(input, cwd, exit_status)?;
+    Ok(())
+}
+
+pub fn run_command_with_output_and_env(
+    input: &[&dyn AsRef<OsStr>],
+    cwd: Option<&Path>,
+    env: Option<&HashMap<String, String>>,
+) -> Result<(), String> {
+    let exit_status = get_command_inner(input, cwd, env)
+        .spawn()
+        .map_err(|e| command_error(input, &cwd, e))?
+        .wait()
+        .map_err(|e| command_error(input, &cwd, e))?;
+    check_exit_status(input, cwd, exit_status)?;
+    Ok(())
+}
+
+pub fn cargo_install(to_install: &str) -> Result<(), String> {
+    let output = run_command(&[&"cargo", &"install", &"--list"], None)?;
+
+    let to_install_needle = format!("{to_install} ");
+    // cargo install --list returns something like this:
+    //
+    // mdbook-toc v0.8.0:
+    //     mdbook-toc
+    // rust-reduce v0.1.0:
+    //     rust-reduce
+    //
+    // We are only interested into the command name so we only look for lines ending with `:`.
+    if String::from_utf8(output.stdout)
+        .unwrap()
+        .lines()
+        .any(|line| line.ends_with(':') && line.starts_with(&to_install_needle))
+    {
+        return Ok(());
+    }
+    // We voluntarily ignore this error.
+    if run_command_with_output(&[&"cargo", &"install", &to_install], None).is_err() {
+        println!("Skipping installation of `{to_install}`");
+    }
+    Ok(())
+}
+
+pub fn get_os_name() -> Result<String, String> {
+    let output = run_command(&[&"uname"], None)?;
+    let name = std::str::from_utf8(&output.stdout)
+        .unwrap_or("")
+        .trim()
+        .to_string();
+    if !name.is_empty() {
+        Ok(name)
+    } else {
+        Err("Failed to retrieve the OS name".to_string())
+    }
+}
+
+pub fn get_rustc_host_triple() -> Result<String, String> {
+    let output = run_command(&[&"rustc", &"-vV"], None)?;
+    let content = std::str::from_utf8(&output.stdout).unwrap_or("");
+
+    for line in content.split('\n').map(|line| line.trim()) {
+        if !line.starts_with("host:") {
+            continue;
+        }
+        return Ok(line.split(':').nth(1).unwrap().trim().to_string());
+    }
+    Err("Cannot find host triple".to_string())
+}
+
+pub fn get_gcc_path() -> Result<String, String> {
+    let content = match fs::read_to_string("gcc_path") {
+        Ok(content) => content,
+        Err(_) => {
+            return Err(
+                "Please put the path to your custom build of libgccjit in the file \
+                   `gcc_path`, see Readme.md for details"
+                    .into(),
+            )
+        }
+    };
+    match content
+        .split('\n')
+        .map(|line| line.trim())
+        .filter(|line| !line.is_empty())
+        .next()
+    {
+        Some(gcc_path) => {
+            let path = Path::new(gcc_path);
+            if !path.exists() {
+                Err(format!(
+                    "Path `{}` contained in the `gcc_path` file doesn't exist",
+                    gcc_path,
+                ))
+            } else {
+                Ok(gcc_path.into())
+            }
+        }
+        None => Err("No path found in `gcc_path` file".into()),
+    }
+}
+
+pub struct CloneResult {
+    pub ran_clone: bool,
+    pub repo_name: String,
+}
+
+pub fn git_clone(to_clone: &str, dest: Option<&Path>) -> Result<CloneResult, String> {
+    let repo_name = to_clone.split('/').last().unwrap();
+    let repo_name = match repo_name.strip_suffix(".git") {
+        Some(n) => n.to_string(),
+        None => repo_name.to_string(),
+    };
+
+    let dest = dest
+        .map(|dest| dest.join(&repo_name))
+        .unwrap_or_else(|| Path::new(&repo_name).into());
+    if dest.is_dir() {
+        return Ok(CloneResult {
+            ran_clone: false,
+            repo_name,
+        });
+    }
+
+    run_command_with_output(&[&"git", &"clone", &to_clone, &dest], None)?;
+    Ok(CloneResult {
+        ran_clone: true,
+        repo_name,
+    })
+}
+
+pub fn walk_dir<P, D, F>(dir: P, mut dir_cb: D, mut file_cb: F) -> Result<(), String>
+where
+    P: AsRef<Path>,
+    D: FnMut(&Path) -> Result<(), String>,
+    F: FnMut(&Path) -> Result<(), String>,
+{
+    let dir = dir.as_ref();
+    for entry in fs::read_dir(dir)
+        .map_err(|error| format!("Failed to read dir `{}`: {:?}", dir.display(), error))?
+    {
+        let entry = entry
+            .map_err(|error| format!("Failed to read entry in `{}`: {:?}", dir.display(), error))?;
+        let entry_path = entry.path();
+        if entry_path.is_dir() {
+            dir_cb(&entry_path)?;
+        } else {
+            file_cb(&entry_path)?;
+        }
+    }
+    Ok(())
+}
diff --git a/compiler/rustc_codegen_gcc/config.sh b/compiler/rustc_codegen_gcc/config.sh
index 166e83901c4..c686df0c72a 100644
--- a/compiler/rustc_codegen_gcc/config.sh
+++ b/compiler/rustc_codegen_gcc/config.sh
@@ -38,10 +38,17 @@ if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
    fi
 fi
 
-export RUSTFLAGS="$CG_RUSTFLAGS $linker -Csymbol-mangling-version=v0 -Cdebuginfo=2 -Clto=off -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot $TEST_FLAGS"
+# Since we don't support ThinLTO, disable LTO completely when not trying to do LTO.
+# TODO(antoyo): remove when we can handle ThinLTO.
+disable_lto_flags=''
+if [[ ! -v FAT_LTO ]]; then
+    disable_lto_flags='-Clto=off'
+fi
+
+export RUSTFLAGS="$CG_RUSTFLAGS $linker -Csymbol-mangling-version=v0 -Cdebuginfo=2 $disable_lto_flags -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot $TEST_FLAGS"
 
 # FIXME(antoyo): remove once the atomic shim is gone
-if [[ `uname` == 'Darwin' ]]; then
+if [[ unamestr == 'Darwin' ]]; then
    export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"
 fi
 
@@ -50,3 +57,7 @@ export RUSTC_LOG=warn # display metadata load errors
 
 export LD_LIBRARY_PATH="$(pwd)/target/out:$(pwd)/build_sysroot/sysroot/lib/rustlib/$TARGET_TRIPLE/lib:$GCC_PATH"
 export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
+# NOTE: To avoid the -fno-inline errors, use /opt/gcc/bin/gcc instead of cc.
+# To do so, add a symlink for cc to /opt/gcc/bin/gcc in our PATH.
+# Another option would be to add the following Rust flag: -Clinker=/opt/gcc/bin/gcc
+export PATH="/opt/gcc/bin:$PATH"
diff --git a/compiler/rustc_codegen_gcc/doc/add-attribute.md b/compiler/rustc_codegen_gcc/doc/add-attribute.md
new file mode 100644
index 00000000000..ae3bcc5e2eb
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/doc/add-attribute.md
@@ -0,0 +1,17 @@
+# Add support for a new function attribute
+
+To add support for a new function attribute in libgccjit, you need to do the following steps:
+
+ 1. Copy the corresponding function from `c-family/c-attribs.cc` into `jit/dummy-frontend.cc`. For example if you add the `target` attribute, the function name will be `handle_target_attribute`.
+ 2. Copy the corresponding entry from the `c_common_attribute_table` variable in the `c-family/c-attribs.cc` file into the `jit_attribute_table` variable in `jit/dummy-frontend.cc`.
+ 3. Add a new variant in the `gcc_jit_fn_attribute` enum in the `jit/libgccjit.h` file.
+ 4. Add a test to ensure the attribute is correctly applied in `gcc/testsuite/jit.dg/`. Take a look at `gcc/testsuite/jit.dg/test-nonnull.c` if you want an example.
+ 5. Run the example like this (in your `gcc-build` folder): `make check-jit RUNTESTFLAGS="-v -v -v jit.exp=jit.dg/test-nonnull.c"`
+
+Once done, you need to update the [gccjit.rs] crate to add the new enum variant in the corresponding enum (`FnAttribute`).
+
+Finally, you need to update this repository by calling the relevant API you added in [gccjit.rs].
+
+To test it, build `gcc`, run `cargo update -p gccjit` and then you can test the generated output for a given Rust crate.
+
+[gccjit.rs]: https://github.com/antoyo/gccjit.rs
diff --git a/compiler/rustc_codegen_gcc/doc/gimple.md b/compiler/rustc_codegen_gcc/doc/gimple.md
new file mode 100644
index 00000000000..145c4eda3c1
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/doc/gimple.md
@@ -0,0 +1,111 @@
+# GIMPLE
+
+You can see the full documentation about what GIMPLE is [here](https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html). In this document we will explain how to generate it.
+
+First, we'll copy the content from `gcc/gcc/testsuite/jit.dg/test-const-attribute.c` into a
+file named `local.c` and remove the content we're not interested into:
+
+```diff
+- /* { dg-do compile { target x86_64-*-* } } */
+...
+- /* We don't want set_options() in harness.h to set -O3 to see that the const
+-    attribute affects the optimizations. */
+- #define TEST_ESCHEWS_SET_OPTIONS
+- static void set_options (gcc_jit_context *ctxt, const char *argv0)
+- {
+-   // Set "-O3".
+-   gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+- }
+-
+- #define TEST_COMPILING_TO_FILE
+- #define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+- #define OUTPUT_FILENAME  "output-of-test-const-attribute.c.s"
+- #include "harness.h"
+...
+- /* { dg-final { jit-verify-output-file-was-created "" } } */
+- /* Check that the loop was optimized away */
+- /* { dg-final { jit-verify-assembler-output-not "jne" } } */
+```
+
+Then we'll add a `main` function which will call the `create_code` function but
+also add the calls we need to generate the GIMPLE:
+
+```C
+int main() {
+    gcc_jit_context *ctxt = gcc_jit_context_acquire();
+    // To set `-O3`, update it depending on your needs.
+    gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+    // Very important option to generate the gimple format.
+    gcc_jit_context_set_bool_option(ctxt, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1);
+    create_code(ctxt, NULL);
+
+    gcc_jit_context_compile(ctxt);
+    // If you want to compile to assembly (or any other format) directly, you can
+    // use the following call instead:
+    // gcc_jit_context_compile_to_file(ctxt, GCC_JIT_OUTPUT_KIND_ASSEMBLER, "out.s");
+
+    return 0;
+}
+```
+
+Then we can compile it by using:
+
+```console
+gcc local.c -I `pwd`/gcc/gcc/jit/ -L `pwd`/gcc-build/gcc -lgccjit -o out
+```
+
+And finally when you run it:
+
+```console
+LD_LIBRARY_PATH=`pwd`/gcc-build/gcc LIBRARY_PATH=`pwd`/gcc-build/gcc ./out
+```
+
+It should display:
+
+```c
+__attribute__((const))
+int xxx ()
+{
+  int D.3394;
+  int sum;
+  int x;
+
+  <D.3377>:
+  x = 45;
+  sum = 0;
+  goto loop_cond;
+  loop_cond:
+  x = x >> 1;
+  if (x != 0) goto after_loop; else goto loop_body;
+  loop_body:
+  _1 = foo (x);
+  _2 = _1 * 2;
+  x = x + _2;
+  goto loop_cond;
+  after_loop:
+  D.3394 = sum;
+  return D.3394;
+}
+```
+
+An alternative way to generate the GIMPLE is to replace:
+
+```c
+    gcc_jit_context_set_bool_option(ctxt, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1);
+```
+
+with:
+
+```c
+    gcc_jit_context_add_command_line_option(ctxt, "-fdump-tree-gimple");
+```
+
+(although you can have both at the same time too). Then you can compile it like previously. Only one difference: before executing it, I recommend to run:
+
+```console
+rm -rf /tmp/libgccjit-*
+```
+
+to make it easier for you to know which folder to look into.
+
+Once the execution is done, you should now have a file with path looking like `/tmp/libgccjit-9OFqkD/fake.c.006t.gimple` which contains the GIMPLE format.
diff --git a/compiler/rustc_codegen_gcc/doc/sending-gcc-patch.md b/compiler/rustc_codegen_gcc/doc/sending-gcc-patch.md
new file mode 100644
index 00000000000..7a47ef29f3c
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/doc/sending-gcc-patch.md
@@ -0,0 +1,44 @@
+This guide explains what to do to send a GCC patch for review.
+
+All the commands are supposed to be run in the folder where you cloned GCC.
+
+```bash
+./contrib/gcc-changelog/git_check_commit.py
+```
+
+You can provide a specific commit hash:
+
+```bash
+./contrib/gcc-changelog/git_check_commit.py abdef78989
+```
+
+a range:
+
+```bash
+./contrib/gcc-changelog/git_check_commit.py HEAD~2
+```
+
+or even a comparison with a remote branch:
+
+```bash
+./contrib/gcc-changelog/git_check_commit.py upstream/master..HEAD
+```
+
+When there is no more errors, generate the git patch:
+
+```bash
+git format-patch -1 `git rev-parse --short HEAD`
+```
+
+Then you can run the remaining checks using:
+
+```bash
+contrib/check_GNU_style.sh 0001-your-patch.patch
+```
+
+When you have no more errors, you can send the `.patch` file to GCC by sending an
+email to `gcc-patches@gcc.gnu.org` and to the relevant GCC mailing lists
+depending on what your patch changes. You can find the list of the mailing lists
+[here](https://gcc.gnu.org/lists.html).
+
+You can find more information about "contributing to GCC" [here](https://gcc.gnu.org/contribute.html).
diff --git a/compiler/rustc_codegen_gcc/example/alloc_example.rs b/compiler/rustc_codegen_gcc/example/alloc_example.rs
index 754e7931412..f1954a30cf8 100644
--- a/compiler/rustc_codegen_gcc/example/alloc_example.rs
+++ b/compiler/rustc_codegen_gcc/example/alloc_example.rs
@@ -1,5 +1,6 @@
 #![feature(start, core_intrinsics, alloc_error_handler, lang_items)]
 #![no_std]
+#![allow(internal_features)]
 
 extern crate alloc;
 extern crate alloc_system;
diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs
index 3deef419f42..56ff84e4bdf 100644
--- a/compiler/rustc_codegen_gcc/example/alloc_system.rs
+++ b/compiler/rustc_codegen_gcc/example/alloc_system.rs
@@ -12,7 +12,7 @@
               target_arch = "mips",
               target_arch = "mips32r6",
               target_arch = "powerpc",
-              target_arch = "csky"
+              target_arch = "csky",
               target_arch = "powerpc64"))]
 const MIN_ALIGN: usize = 8;
 #[cfg(any(target_arch = "x86_64",
diff --git a/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs
index 3af0ba09e0b..b299aa87974 100644
--- a/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs
+++ b/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs
@@ -2,6 +2,7 @@
 
 #![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)]
 #![feature(rustc_attrs)]
+#![allow(internal_features)]
 
 use std::{
     ops::{Deref, CoerceUnsized, DispatchFromDyn},
diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs
index 0cd7e6047c2..34328520343 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core.rs
@@ -4,7 +4,7 @@
     thread_local
 )]
 #![no_core]
-#![allow(dead_code)]
+#![allow(dead_code, internal_features)]
 
 #[no_mangle]
 unsafe extern "C" fn _Unwind_Resume() {
@@ -429,6 +429,15 @@ fn panic_cannot_unwind() -> ! {
     }
 }
 
+#[lang = "panic_in_cleanup"]
+#[rustc_nounwind]
+fn panic_in_cleanup() -> ! {
+    unsafe {
+        libc::printf("panic in a destructor during cleanup\n\0" as *const str as *const i8);
+        intrinsics::abort();
+    }
+}
+
 #[lang = "panic_bounds_check"]
 #[track_caller]
 fn panic_bounds_check(index: usize, len: usize) -> ! {
diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
index b93d6859706..c3aea571815 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
@@ -5,7 +5,7 @@
     extern_types, thread_local
 )]
 #![no_core]
-#![allow(dead_code, non_camel_case_types)]
+#![allow(dead_code, internal_features, non_camel_case_types)]
 
 extern crate mini_core;
 
diff --git a/compiler/rustc_codegen_gcc/example/mod_bench.rs b/compiler/rustc_codegen_gcc/example/mod_bench.rs
index 5e2e7f25a2c..c60bc7fb724 100644
--- a/compiler/rustc_codegen_gcc/example/mod_bench.rs
+++ b/compiler/rustc_codegen_gcc/example/mod_bench.rs
@@ -1,5 +1,6 @@
 #![feature(start, core_intrinsics, lang_items)]
 #![no_std]
+#![allow(internal_features)]
 
 #[link(name = "c")]
 extern {}
diff --git a/compiler/rustc_codegen_gcc/failing-lto-tests.txt b/compiler/rustc_codegen_gcc/failing-lto-tests.txt
new file mode 100644
index 00000000000..2e0b6134070
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/failing-lto-tests.txt
@@ -0,0 +1,23 @@
+tests/ui/lint/unsafe_code/forge_unsafe_block.rs
+tests/ui/lint/unused-qualification-in-derive-expansion.rs
+tests/ui/macro-quote-test.rs
+tests/ui/macros/proc_macro.rs
+tests/ui/panic-runtime/lto-unwind.rs
+tests/ui/resolve/derive-macro-1.rs
+tests/ui/resolve/derive-macro-2.rs
+tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-pretty.rs
+tests/ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs
+tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs
+tests/ui/rust-2018/suggestions-not-always-applicable.rs
+tests/ui/rust-2021/reserved-prefixes-via-macro.rs
+tests/ui/underscore-imports/duplicate.rs
+tests/ui/async-await/issues/issue-60674.rs
+tests/ui/attributes/main-removed-2/main.rs
+tests/ui/cfg/assume-incomplete-release/assume-incomplete.rs
+tests/ui/crate-loading/cross-compiled-proc-macro.rs
+tests/ui/derives/derive-marker-tricky.rs
+tests/ui/diagnostic_namespace/existing_proc_macros.rs
+tests/ui/fmt/format-args-capture-issue-106408.rs
+tests/ui/fmt/indoc-issue-106408.rs
+tests/ui/hygiene/issue-77523-def-site-async-await.rs
+tests/ui/inherent-impls-overlap-check/no-overlap.rs
diff --git a/compiler/rustc_codegen_gcc/failing-non-lto-tests.txt b/compiler/rustc_codegen_gcc/failing-non-lto-tests.txt
new file mode 100644
index 00000000000..2f338f7d3c8
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/failing-non-lto-tests.txt
@@ -0,0 +1,11 @@
+tests/ui/issues/issue-44056.rs
+tests/ui/lto/fat-lto.rs
+tests/ui/lto/debuginfo-lto.rs
+tests/ui/lto/lto-many-codegen-units.rs
+tests/ui/lto/issue-100772.rs
+tests/ui/lto/lto-rustc-loads-linker-plugin.rs
+tests/ui/panic-runtime/lto-unwind.rs
+tests/ui/sanitize/issue-111184-generator-witness.rs
+tests/ui/sepcomp/sepcomp-lib-lto.rs
+tests/ui/lto/lto-opt-level-s.rs
+tests/ui/lto/lto-opt-level-z.rs
diff --git a/compiler/rustc_codegen_gcc/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/failing-ui-tests.txt
index 801464daae9..ed56a11a170 100644
--- a/compiler/rustc_codegen_gcc/failing-ui-tests.txt
+++ b/compiler/rustc_codegen_gcc/failing-ui-tests.txt
@@ -1,11 +1,5 @@
-tests/ui/allocator/custom-in-block.rs
-tests/ui/allocator/custom-in-submodule.rs
-tests/ui/allocator/custom.rs
-tests/ui/allocator/hygiene.rs
 tests/ui/allocator/no_std-alloc-error-handler-custom.rs
 tests/ui/allocator/no_std-alloc-error-handler-default.rs
-tests/ui/allocator/xcrate-use.rs
-tests/ui/allocator/xcrate-use2.rs
 tests/ui/asm/may_unwind.rs
 tests/ui/asm/x86_64/multiple-clobber-abi.rs
 tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs
@@ -14,15 +8,12 @@ tests/ui/linkage-attr/linkage1.rs
 tests/ui/lto/dylib-works.rs
 tests/ui/numbers-arithmetic/saturating-float-casts.rs
 tests/ui/polymorphization/promoted-function.rs
-tests/ui/process/nofile-limit.rs
 tests/ui/sepcomp/sepcomp-cci.rs
 tests/ui/sepcomp/sepcomp-extern.rs
 tests/ui/sepcomp/sepcomp-fns-backwards.rs
 tests/ui/sepcomp/sepcomp-fns.rs
 tests/ui/sepcomp/sepcomp-statics.rs
 tests/ui/simd/intrinsic/generic-arithmetic-pass.rs
-tests/ui/sse2.rs
-tests/ui/target-feature/missing-plusminus.rs
 tests/ui/asm/x86_64/may_unwind.rs
 tests/ui/backtrace.rs
 tests/ui/catch-unwind-bang.rs
@@ -54,8 +45,8 @@ tests/ui/issues/issue-40883.rs
 tests/ui/issues/issue-43853.rs
 tests/ui/issues/issue-47364.rs
 tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs
-tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs
 tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs
+tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs
 tests/ui/simd/issue-17170.rs
 tests/ui/simd/issue-39720.rs
 tests/ui/simd/issue-89193.rs
@@ -65,6 +56,18 @@ tests/ui/alloc-error/default-alloc-error-hook.rs
 tests/ui/generator/panic-safe.rs
 tests/ui/issues/issue-14875.rs
 tests/ui/issues/issue-29948.rs
-tests/ui/panic-while-printing.rs
-tests/ui/enum-discriminant/get_discr.rs
 tests/ui/panics/nested_panic_caught.rs
+tests/ui/simd/intrinsic/generic-bswap-byte.rs
+tests/ui/const_prop/ice-issue-111353.rs
+tests/ui/process/println-with-broken-pipe.rs
+tests/ui/panic-runtime/lto-abort.rs
+tests/ui/lto/thin-lto-inlines2.rs
+tests/ui/lto/weak-works.rs
+tests/ui/lto/thin-lto-inlines.rs
+tests/ui/lto/thin-lto-global-allocator.rs
+tests/ui/lto/msvc-imp-present.rs
+tests/ui/lto/lto-thin-rustc-loads-linker-plugin.rs
+tests/ui/lto/all-crates.rs
+tests/ui/async-await/deep-futures-are-freeze.rs
+tests/ui/closures/capture-unsized-by-ref.rs
+tests/ui/generator/resume-after-return.rs
diff --git a/compiler/rustc_codegen_gcc/failing-ui-tests12.txt b/compiler/rustc_codegen_gcc/failing-ui-tests12.txt
index 8c27bd8b8ca..0ac0a034af4 100644
--- a/compiler/rustc_codegen_gcc/failing-ui-tests12.txt
+++ b/compiler/rustc_codegen_gcc/failing-ui-tests12.txt
@@ -37,3 +37,4 @@ tests/ui/simd/intrinsic/generic-gather-pass.rs
 tests/ui/simd/issue-85915-simd-ptrs.rs
 tests/ui/issues/issue-68010-large-zst-consts.rs
 tests/ui/rust-2018/proc-macro-crate-in-paths.rs
+tests/ui/target-feature/missing-plusminus.rs
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 2fd0daee3e7..5ca0a2e1b6d 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -1,3 +1,7 @@
+codegen_gcc_unknown_ctarget_feature_prefix =
+    unknown feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = features must begin with a `+` to enable or `-` to disable it
+
 codegen_gcc_invalid_minimum_alignment =
     invalid minimum global alignment: {$err}
 
@@ -9,3 +13,29 @@ codegen_gcc_tied_target_features = the target features {$features} must all be e
 
 codegen_gcc_unwinding_inline_asm =
     GCC backend does not support unwinding from inline asm
+
+codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err}
+
+codegen_gcc_dynamic_linking_with_lto =
+    cannot prefer dynamic linking when performing LTO
+    .note = only 'staticlib', 'bin', and 'cdylib' outputs are supported with LTO
+
+codegen_gcc_load_bitcode = failed to load bitcode of module "{$name}"
+
+codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and static library outputs
+
+codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
+
+codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
+
+codegen_gcc_unknown_ctarget_feature =
+    unknown feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = it is still passed through to the codegen backend
+    .possible_feature = you might have meant: `{$rust_feature}`
+    .consider_filing_feature_request = consider filing a feature request
+
+codegen_gcc_missing_features =
+    add the missing features in a `target_feature` attribute
+
+codegen_gcc_target_feature_disable_or_enable =
+    the target features {$features} must all be either enabled or disabled together
diff --git a/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch b/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch
index 93c63b5dcac..2a55f2cb796 100644
--- a/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch
+++ b/compiler/rustc_codegen_gcc/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch
@@ -1,25 +1,26 @@
-From c3821e02fbd6cb5ad6e06d759fccdc9073712375 Mon Sep 17 00:00:00 2001
+From b8f3eed3053c9333b5dfbeaeb2a6a65a4b3156df Mon Sep 17 00:00:00 2001
 From: Antoni Boucher <bouanto@zoho.com>
-Date: Tue, 7 Jun 2022 21:40:13 -0400
-Subject: [PATCH] Add stdarch Cargo.toml for testing
+Date: Tue, 29 Aug 2023 13:06:34 -0400
+Subject: [PATCH] Patch 0001-Add-stdarch-Cargo.toml-for-testing.patch
 
 ---
- library/stdarch/Cargo.toml | 20 ++++++++++++++++++++
- 1 file changed, 20 insertions(+)
+ library/stdarch/Cargo.toml | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
  create mode 100644 library/stdarch/Cargo.toml
 
 diff --git a/library/stdarch/Cargo.toml b/library/stdarch/Cargo.toml
 new file mode 100644
-index 0000000..fbe0a95
+index 0000000..4c63700
 --- /dev/null
 +++ b/library/stdarch/Cargo.toml
-@@ -0,0 +1,20 @@
+@@ -0,0 +1,21 @@
 +[workspace]
++resolver = "1"
 +members = [
 +  "crates/core_arch",
 +  "crates/std_detect",
 +  "crates/stdarch-gen",
-+  "examples/"
++  #"examples/"
 +]
 +exclude = [
 +  "crates/wasm-assert-instr-tests"
@@ -35,5 +36,5 @@ index 0000000..fbe0a95
 +opt-level = 3
 +incremental = true
 -- 
-2.26.2.7.g19db9cfb68.dirty
+2.42.0
 
diff --git a/compiler/rustc_codegen_gcc/patches/0001-Disable-examples.patch b/compiler/rustc_codegen_gcc/patches/0001-Disable-examples.patch
deleted file mode 100644
index 1b71df1ca8d..00000000000
--- a/compiler/rustc_codegen_gcc/patches/0001-Disable-examples.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From a2d53a324a02c04b76c0e9d39dc15cd443a3b8b2 Mon Sep 17 00:00:00 2001
-From: Antoni Boucher <bouanto@zoho.com>
-Date: Fri, 25 Nov 2022 11:18:11 -0500
-Subject: [PATCH] Disable examples
-
----
- library/stdarch/Cargo.toml | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/library/stdarch/Cargo.toml b/library/stdarch/Cargo.toml
-index fbe0a95..748d72d 100644
---- a/library/stdarch/Cargo.toml
-+++ b/library/stdarch/Cargo.toml
-@@ -3,7 +3,7 @@ members = [
-   "crates/core_arch",
-   "crates/std_detect",
-   "crates/stdarch-gen",
--  "examples/"
-+  #"examples/"
- ]
- exclude = [
-   "crates/wasm-assert-instr-tests"
--- 
-2.26.2.7.g19db9cfb68.dirty
-
diff --git a/compiler/rustc_codegen_gcc/prepare.sh b/compiler/rustc_codegen_gcc/prepare.sh
deleted file mode 100755
index e98f24c6e12..00000000000
--- a/compiler/rustc_codegen_gcc/prepare.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-set -e
-set -v
-
-source prepare_build.sh
-
-cargo install hyperfine || echo "Skipping hyperfine install"
-
-git clone https://github.com/rust-random/rand.git || echo "rust-random/rand has already been cloned"
-pushd rand
-git checkout -- .
-git checkout 0f933f9c7176e53b2a3c7952ded484e1783f0bf1
-git am ../crate_patches/*-rand-*.patch
-popd
-
-git clone https://github.com/rust-lang/regex.git || echo "rust-lang/regex has already been cloned"
-pushd regex
-git checkout -- .
-git checkout 341f207c1071f7290e3f228c710817c280c8dca1
-popd
-
-git clone https://github.com/ebobby/simple-raytracer || echo "ebobby/simple-raytracer has already been cloned"
-pushd simple-raytracer
-git checkout -- .
-git checkout 804a7a21b9e673a482797aa289a18ed480e4d813
-
-# build with cg_llvm for perf comparison
-cargo build
-mv target/debug/main raytracer_cg_llvm
-popd
diff --git a/compiler/rustc_codegen_gcc/prepare_build.sh b/compiler/rustc_codegen_gcc/prepare_build.sh
deleted file mode 100755
index 8194360da4b..00000000000
--- a/compiler/rustc_codegen_gcc/prepare_build.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-set -e
-set -v
-
-./build_sysroot/prepare_sysroot_src.sh
diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain
index ebb04d0069c..25a1cea98cc 100644
--- a/compiler/rustc_codegen_gcc/rust-toolchain
+++ b/compiler/rustc_codegen_gcc/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2023-06-19"
+channel = "nightly-2023-10-08"
 components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
diff --git a/compiler/rustc_codegen_gcc/rustup.sh b/compiler/rustc_codegen_gcc/rustup.sh
index 041079bc9c6..a4f938e4b5b 100755
--- a/compiler/rustc_codegen_gcc/rustup.sh
+++ b/compiler/rustc_codegen_gcc/rustup.sh
@@ -16,7 +16,7 @@ case $1 in
         done
 
         ./clean_all.sh
-        ./prepare.sh
+        ./y.sh prepare
         ;;
     "commit")
         git add rust-toolchain
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs
index a49530ebb4c..35bb0b6e5f4 100644
--- a/compiler/rustc_codegen_gcc/src/abi.rs
+++ b/compiler/rustc_codegen_gcc/src/abi.rs
@@ -3,7 +3,9 @@ use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::bug;
 use rustc_middle::ty::Ty;
-use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
+#[cfg(feature = "master")]
+use rustc_session::config;
+use rustc_target::abi::call::{ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind};
 
 use crate::builder::Builder;
 use crate::context::CodegenCx;
@@ -120,30 +122,50 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                 }
             };
 
+        #[cfg(feature = "master")]
+        let apply_attrs = |ty: Type<'gcc>, attrs: &ArgAttributes| {
+            if cx.sess().opts.optimize != config::OptLevel::No
+                && attrs.regular.contains(rustc_target::abi::call::ArgAttribute::NoAlias)
+            {
+                ty.make_restrict()
+            } else {
+                ty
+            }
+        };
+        #[cfg(not(feature = "master"))]
+        let apply_attrs = |ty: Type<'gcc>, _attrs: &ArgAttributes| {
+            ty
+        };
+
         for arg in self.args.iter() {
             let arg_ty = match arg.mode {
                 PassMode::Ignore => continue,
-                PassMode::Direct(_) => arg.layout.immediate_gcc_type(cx),
-                PassMode::Pair(..) => {
-                    argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 0));
-                    argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 1));
+                PassMode::Pair(a, b) => {
+                    argument_tys.push(apply_attrs(arg.layout.scalar_pair_element_gcc_type(cx, 0), &a));
+                    argument_tys.push(apply_attrs(arg.layout.scalar_pair_element_gcc_type(cx, 1), &b));
                     continue;
                 }
-                PassMode::Indirect { meta_attrs: Some(_), .. } => {
-                    unimplemented!();
-                }
                 PassMode::Cast { ref cast, pad_i32 } => {
                     // add padding
                     if pad_i32 {
                         argument_tys.push(Reg::i32().gcc_type(cx));
                     }
-                    cast.gcc_type(cx)
+                    let ty = cast.gcc_type(cx);
+                    apply_attrs(ty, &cast.attrs)
                 }
-                PassMode::Indirect { meta_attrs: None, on_stack: true, .. } => {
+                PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: true } => {
+                    // This is a "byval" argument, so we don't apply the `restrict` attribute on it.
                     on_stack_param_indices.insert(argument_tys.len());
                     arg.memory_ty(cx)
                 },
-                PassMode::Indirect { meta_attrs: None, on_stack: false, .. } => cx.type_ptr_to(arg.memory_ty(cx)),
+                PassMode::Direct(attrs) => apply_attrs(arg.layout.immediate_gcc_type(cx), &attrs),
+                PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => {
+                    apply_attrs(cx.type_ptr_to(arg.memory_ty(cx)), &attrs)
+                }
+                PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => {
+                    assert!(!on_stack);
+                    apply_attrs(apply_attrs(cx.type_ptr_to(arg.memory_ty(cx)), &attrs), &meta_attrs)
+                }
             };
             argument_tys.push(arg_ty);
         }
diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs
index edd7ab722f6..c8c098e2973 100644
--- a/compiler/rustc_codegen_gcc/src/allocator.rs
+++ b/compiler/rustc_codegen_gcc/src/allocator.rs
@@ -1,6 +1,6 @@
 #[cfg(feature="master")]
 use gccjit::FnAttribute;
-use gccjit::{FunctionType, GlobalKind, ToRValue};
+use gccjit::{Context, FunctionType, GlobalKind, ToRValue, Type};
 use rustc_ast::expand::allocator::{
     alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
     ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
@@ -22,7 +22,6 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
         };
     let i8 = context.new_type::<i8>();
     let i8p = i8.make_pointer();
-    let void = context.new_type::<()>();
 
     if kind == AllocatorKind::Default {
         for method in ALLOCATOR_METHODS {
@@ -47,67 +46,62 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
                     panic!("invalid allocator output")
                 }
             };
-            let name = global_fn_name(method.name);
+            let from_name = global_fn_name(method.name);
+            let to_name = default_fn_name(method.name);
 
-            let args: Vec<_> = types.iter().enumerate()
-                .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
-                .collect();
-            let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false);
+            create_wrapper_function(tcx, context, &from_name, &to_name, &types, output);
+        }
+    }
 
-            if tcx.sess.target.options.default_hidden_visibility {
-                #[cfg(feature="master")]
-                func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
-            }
-            if tcx.sess.must_emit_unwind_tables() {
-                // TODO(antoyo): emit unwind tables.
-            }
+    // FIXME(bjorn3): Add noreturn attribute
+    create_wrapper_function(
+        tcx,
+        context,
+        "__rust_alloc_error_handler",
+        &alloc_error_handler_name(alloc_error_handler_kind),
+        &[usize, usize],
+        None,
+    );
 
-            let callee = default_fn_name(method.name);
-            let args: Vec<_> = types.iter().enumerate()
-                .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
-                .collect();
-            let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false);
-            #[cfg(feature="master")]
-            callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
-
-            let block = func.new_block("entry");
-
-            let args = args
-                .iter()
-                .enumerate()
-                .map(|(i, _)| func.get_param(i as i32).to_rvalue())
-                .collect::<Vec<_>>();
-            let ret = context.new_call(None, callee, &args);
-            //llvm::LLVMSetTailCall(ret, True);
-            if output.is_some() {
-                block.end_with_return(None, ret);
-            }
-            else {
-                block.end_with_void_return(None);
-            }
+    let name = OomStrategy::SYMBOL.to_string();
+    let global = context.new_global(None, GlobalKind::Exported, i8, name);
+    let value = tcx.sess.opts.unstable_opts.oom.should_panic();
+    let value = context.new_rvalue_from_int(i8, value as i32);
+    global.global_set_initializer_rvalue(value);
 
-            // TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
-            // as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
-        }
-    }
+    let name = NO_ALLOC_SHIM_IS_UNSTABLE.to_string();
+    let global = context.new_global(None, GlobalKind::Exported, i8, name);
+    let value = context.new_rvalue_from_int(i8, 0);
+    global.global_set_initializer_rvalue(value);
+}
+
+fn create_wrapper_function(
+    tcx: TyCtxt<'_>,
+    context: &Context<'_>,
+    from_name: &str,
+    to_name: &str,
+    types: &[Type<'_>],
+    output: Option<Type<'_>>,
+) {
+    let void = context.new_type::<()>();
 
-    let types = [usize, usize];
-    let name = "__rust_alloc_error_handler".to_string();
     let args: Vec<_> = types.iter().enumerate()
         .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
         .collect();
-    let func = context.new_function(None, FunctionType::Exported, void, &args, name, false);
+    let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, from_name, false);
 
-    if tcx.sess.target.default_hidden_visibility {
+    if tcx.sess.target.options.default_hidden_visibility {
         #[cfg(feature="master")]
         func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
     }
+    if tcx.sess.must_emit_unwind_tables() {
+        // TODO(antoyo): emit unwind tables.
+    }
 
-    let callee = alloc_error_handler_name(alloc_error_handler_kind);
     let args: Vec<_> = types.iter().enumerate()
         .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
         .collect();
-    let callee = context.new_function(None, FunctionType::Extern, void, &args, callee, false);
+    let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, to_name, false);
     #[cfg(feature="master")]
     callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
 
@@ -118,18 +112,15 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
         .enumerate()
         .map(|(i, _)| func.get_param(i as i32).to_rvalue())
         .collect::<Vec<_>>();
-    let _ret = context.new_call(None, callee, &args);
+    let ret = context.new_call(None, callee, &args);
     //llvm::LLVMSetTailCall(ret, True);
-    block.end_with_void_return(None);
-
-    let name = OomStrategy::SYMBOL.to_string();
-    let global = context.new_global(None, GlobalKind::Exported, i8, name);
-    let value = tcx.sess.opts.unstable_opts.oom.should_panic();
-    let value = context.new_rvalue_from_int(i8, value as i32);
-    global.global_set_initializer_rvalue(value);
+    if output.is_some() {
+        block.end_with_return(None, ret);
+    }
+    else {
+        block.end_with_void_return(None);
+    }
 
-    let name = NO_ALLOC_SHIM_IS_UNSTABLE.to_string();
-    let global = context.new_global(None, GlobalKind::Exported, i8, name);
-    let value = context.new_rvalue_from_int(i8, 0);
-    global.global_set_initializer_rvalue(value);
+    // TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
+    // as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
 }
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index 905fdac92e9..f3a9ca77a67 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -452,10 +452,6 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
                         }
 
                         InlineAsmOperandRef::Const { ref string } => {
-                            // Const operands get injected directly into the template
-                            if att_dialect {
-                                template_str.push('$');
-                            }
                             template_str.push_str(string);
                         }
                     }
diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs
index eb0cce19b85..971e019a4f6 100644
--- a/compiler/rustc_codegen_gcc/src/attributes.rs
+++ b/compiler/rustc_codegen_gcc/src/attributes.rs
@@ -4,72 +4,13 @@ use gccjit::Function;
 use rustc_attr::InstructionSetAttr;
 #[cfg(feature="master")]
 use rustc_attr::InlineAttr;
-use rustc_codegen_ssa::target_features::tied_target_features;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::ty;
 #[cfg(feature="master")]
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_session::Session;
 use rustc_span::symbol::sym;
-use smallvec::{smallvec, SmallVec};
 
 use crate::{context::CodegenCx, errors::TiedTargetFeatures};
-
-// Given a map from target_features to whether they are enabled or disabled,
-// ensure only valid combinations are allowed.
-pub fn check_tied_features(sess: &Session, features: &FxHashMap<&str, bool>) -> Option<&'static [&'static str]> {
-    for tied in tied_target_features(sess) {
-        // Tied features must be set to the same value, or not set at all
-        let mut tied_iter = tied.iter();
-        let enabled = features.get(tied_iter.next().unwrap());
-        if tied_iter.any(|feature| enabled != features.get(feature)) {
-            return Some(tied);
-        }
-    }
-    None
-}
-
-// TODO(antoyo): maybe move to a new module gcc_util.
-// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
-fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
-    let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
-    match (arch, s) {
-        ("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
-        ("x86", "pclmulqdq") => smallvec!["pclmul"],
-        ("x86", "rdrand") => smallvec!["rdrnd"],
-        ("x86", "bmi1") => smallvec!["bmi"],
-        ("x86", "cmpxchg16b") => smallvec!["cx16"],
-        ("x86", "avx512vaes") => smallvec!["vaes"],
-        ("x86", "avx512gfni") => smallvec!["gfni"],
-        ("x86", "avx512vpclmulqdq") => smallvec!["vpclmulqdq"],
-        // NOTE: seems like GCC requires 'avx512bw' for 'avx512vbmi2'.
-        ("x86", "avx512vbmi2") => smallvec!["avx512vbmi2", "avx512bw"],
-        // NOTE: seems like GCC requires 'avx512bw' for 'avx512bitalg'.
-        ("x86", "avx512bitalg") => smallvec!["avx512bitalg", "avx512bw"],
-        ("aarch64", "rcpc2") => smallvec!["rcpc-immo"],
-        ("aarch64", "dpb") => smallvec!["ccpp"],
-        ("aarch64", "dpb2") => smallvec!["ccdp"],
-        ("aarch64", "frintts") => smallvec!["fptoint"],
-        ("aarch64", "fcma") => smallvec!["complxnum"],
-        ("aarch64", "pmuv3") => smallvec!["perfmon"],
-        ("aarch64", "paca") => smallvec!["pauth"],
-        ("aarch64", "pacg") => smallvec!["pauth"],
-        // Rust ties fp and neon together. In LLVM neon implicitly enables fp,
-        // but we manually enable neon when a feature only implicitly enables fp
-        ("aarch64", "f32mm") => smallvec!["f32mm", "neon"],
-        ("aarch64", "f64mm") => smallvec!["f64mm", "neon"],
-        ("aarch64", "fhm") => smallvec!["fp16fml", "neon"],
-        ("aarch64", "fp16") => smallvec!["fullfp16", "neon"],
-        ("aarch64", "jsconv") => smallvec!["jsconv", "neon"],
-        ("aarch64", "sve") => smallvec!["sve", "neon"],
-        ("aarch64", "sve2") => smallvec!["sve2", "neon"],
-        ("aarch64", "sve2-aes") => smallvec!["sve2-aes", "neon"],
-        ("aarch64", "sve2-sm4") => smallvec!["sve2-sm4", "neon"],
-        ("aarch64", "sve2-sha3") => smallvec!["sve2-sha3", "neon"],
-        ("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
-        (_, s) => smallvec![s],
-    }
-}
+use crate::gcc_util::{check_tied_features, to_gcc_features};
 
 /// Get GCC attribute for the provided inline heuristic.
 #[cfg(feature="master")]
@@ -114,6 +55,19 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
         if let Some(attr) = inline_attr(cx, inline) {
             func.add_attribute(attr);
         }
+
+        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
+            func.add_attribute(FnAttribute::Cold);
+        }
+        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
+            func.add_attribute(FnAttribute::ReturnsTwice);
+        }
+        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
+            func.add_attribute(FnAttribute::Pure);
+        }
+        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
+            func.add_attribute(FnAttribute::Const);
+        }
     }
 
     let function_features =
@@ -140,11 +94,33 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
         }))
         .collect::<Vec<_>>();
 
-    // TODO(antoyo): check if we really need global backend features. (Maybe they could be applied
-    // globally?)
+    // TODO(antoyo): cg_llvm adds global features to each function so that LTO keep them.
+    // Check if GCC requires the same.
     let mut global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
     function_features.extend(&mut global_features);
-    let target_features = function_features.join(",");
+    let target_features = function_features
+        .iter()
+        .filter_map(|feature| {
+            // FIXME(antoyo): for some reasons, disabling SSE results in the following error when
+            // compiling Rust for Linux:
+            // SSE register return with SSE disabled
+            // TODO(antoyo): support soft-float and retpoline-external-thunk.
+            if feature.contains("soft-float") || feature.contains("retpoline-external-thunk") || *feature == "-sse" {
+                return None;
+            }
+
+            if feature.starts_with('-') {
+                Some(format!("no{}", feature))
+            }
+            else if feature.starts_with('+') {
+                Some(feature[1..].to_string())
+            }
+            else {
+                Some(feature.to_string())
+            }
+        })
+        .collect::<Vec<_>>()
+        .join(",");
     if !target_features.is_empty() {
         #[cfg(feature="master")]
         func.add_attribute(FnAttribute::Target(&target_features));
diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs
new file mode 100644
index 00000000000..529454b119e
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/src/back/lto.rs
@@ -0,0 +1,341 @@
+/// GCC requires to use the same toolchain for the whole compilation when doing LTO.
+/// So, we need the same version/commit of the linker (gcc) and lto front-end binaries (lto1,
+/// lto-wrapper, liblto_plugin.so).
+
+// FIXME(antoyo): the executables compiled with LTO are bigger than those compiled without LTO.
+// Since it is the opposite for cg_llvm, check if this is normal.
+//
+// Maybe we embed the bitcode in the final binary?
+// It doesn't look like we try to generate fat objects for the final binary.
+// Check if the way we combine the object files make it keep the LTO sections on the final link.
+// Maybe that's because the combined object files contain the IR (true) and the final link
+// does not remove it?
+//
+// TODO(antoyo): for performance, check which optimizations the C++ frontend enables.
+//
+// Fix these warnings:
+// /usr/bin/ld: warning: type of symbol `_RNvNvNvNtCs5JWOrf9uCus_5rayon11thread_pool19WORKER_THREAD_STATE7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
+// /usr/bin/ld: warning: type of symbol `_RNvNvNvNvNtNtNtCsAj5i4SGTR7_3std4sync4mpmc5waker17current_thread_id5DUMMY7___getit5___KEY' changed from 1 to 6 in /tmp/ccKeUSiR.ltrans0.ltrans.o
+// /usr/bin/ld: warning: incremental linking of LTO and non-LTO objects; using -flinker-output=nolto-rel which will bypass whole program optimization
+
+use std::ffi::CString;
+use std::fs::{self, File};
+use std::path::{Path, PathBuf};
+
+use gccjit::OutputKind;
+use object::read::archive::ArchiveFile;
+use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule};
+use rustc_codegen_ssa::back::symbol_export;
+use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
+use rustc_codegen_ssa::traits::*;
+use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
+use rustc_data_structures::memmap::Mmap;
+use rustc_errors::{FatalError, Handler};
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_middle::dep_graph::WorkProduct;
+use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
+use rustc_session::config::{CrateType, Lto};
+use tempfile::{TempDir, tempdir};
+
+use crate::back::write::save_temp_bitcode;
+use crate::errors::{
+    DynamicLinkingWithLTO, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
+};
+use crate::{GccCodegenBackend, GccContext, to_gcc_opt_level};
+
+/// We keep track of the computed LTO cache keys from the previous
+/// session to determine which CGUs we can reuse.
+//pub const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
+
+pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
+    match crate_type {
+        CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true,
+        CrateType::Rlib | CrateType::ProcMacro => false,
+    }
+}
+
+struct LtoData {
+    // TODO(antoyo): use symbols_below_threshold.
+    //symbols_below_threshold: Vec<CString>,
+    upstream_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
+    tmp_path: TempDir,
+}
+
+fn prepare_lto(cgcx: &CodegenContext<GccCodegenBackend>, diag_handler: &Handler) -> Result<LtoData, FatalError> {
+    let export_threshold = match cgcx.lto {
+        // We're just doing LTO for our one crate
+        Lto::ThinLocal => SymbolExportLevel::Rust,
+
+        // We're doing LTO for the entire crate graph
+        Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types),
+
+        Lto::No => panic!("didn't request LTO but we're doing LTO"),
+    };
+
+    let tmp_path =
+        match tempdir() {
+            Ok(tmp_path) => tmp_path,
+            Err(error) => {
+                eprintln!("Cannot create temporary directory: {}", error);
+                return Err(FatalError);
+            },
+        };
+
+    let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
+        if info.level.is_below_threshold(export_threshold) || info.used {
+            Some(CString::new(name.as_str()).unwrap())
+        } else {
+            None
+        }
+    };
+    let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
+    let mut symbols_below_threshold = {
+        let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold");
+        exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<CString>>()
+    };
+    info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
+
+    // If we're performing LTO for the entire crate graph, then for each of our
+    // upstream dependencies, find the corresponding rlib and load the bitcode
+    // from the archive.
+    //
+    // We save off all the bytecode and GCC module file path for later processing
+    // with either fat or thin LTO
+    let mut upstream_modules = Vec::new();
+    if cgcx.lto != Lto::ThinLocal {
+        // Make sure we actually can run LTO
+        for crate_type in cgcx.crate_types.iter() {
+            if !crate_type_allows_lto(*crate_type) {
+                diag_handler.emit_err(LtoDisallowed);
+                return Err(FatalError);
+            } else if *crate_type == CrateType::Dylib {
+                if !cgcx.opts.unstable_opts.dylib_lto {
+                    diag_handler.emit_err(LtoDylib);
+                    return Err(FatalError);
+                }
+            }
+        }
+
+        if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
+            diag_handler.emit_err(DynamicLinkingWithLTO);
+            return Err(FatalError);
+        }
+
+        for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
+            let exported_symbols =
+                cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
+            {
+                let _timer =
+                    cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold");
+                symbols_below_threshold
+                    .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
+            }
+
+            let archive_data = unsafe {
+                Mmap::map(File::open(&path).expect("couldn't open rlib"))
+                    .expect("couldn't map rlib")
+            };
+            let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
+            let obj_files = archive
+                .members()
+                .filter_map(|child| {
+                    child.ok().and_then(|c| {
+                        std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))
+                    })
+                })
+                .filter(|&(name, _)| looks_like_rust_object_file(name));
+            for (name, child) in obj_files {
+                info!("adding bitcode from {}", name);
+                let path = tmp_path.path().join(name);
+                match save_as_file(child.data(&*archive_data).expect("corrupt rlib"), &path) {
+                    Ok(()) => {
+                        let buffer = ModuleBuffer::new(path);
+                        let module = SerializedModule::Local(buffer);
+                        upstream_modules.push((module, CString::new(name).unwrap()));
+                    }
+                    Err(e) => {
+                        diag_handler.emit_err(e);
+                        return Err(FatalError);
+                    }
+                }
+            }
+        }
+    }
+
+    Ok(LtoData {
+        //symbols_below_threshold,
+        upstream_modules,
+        tmp_path,
+    })
+}
+
+fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> {
+    fs::write(path, obj)
+        .map_err(|error| LtoBitcodeFromRlib {
+            gcc_err: format!("write object file to temp dir: {}", error)
+        })
+}
+
+/// Performs fat LTO by merging all modules into a single one and returning it
+/// for further optimization.
+pub(crate) fn run_fat(
+    cgcx: &CodegenContext<GccCodegenBackend>,
+    modules: Vec<FatLtoInput<GccCodegenBackend>>,
+    cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
+) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> {
+    let diag_handler = cgcx.create_diag_handler();
+    let lto_data = prepare_lto(cgcx, &diag_handler)?;
+    /*let symbols_below_threshold =
+        lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();*/
+    fat_lto(cgcx, &diag_handler, modules, cached_modules, lto_data.upstream_modules, lto_data.tmp_path,
+        //&symbols_below_threshold,
+    )
+}
+
+fn fat_lto(cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, modules: Vec<FatLtoInput<GccCodegenBackend>>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, tmp_path: TempDir,
+    //symbols_below_threshold: &[*const libc::c_char],
+) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> {
+    let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module");
+    info!("going for a fat lto");
+
+    // Sort out all our lists of incoming modules into two lists.
+    //
+    // * `serialized_modules` (also and argument to this function) contains all
+    //   modules that are serialized in-memory.
+    // * `in_memory` contains modules which are already parsed and in-memory,
+    //   such as from multi-CGU builds.
+    //
+    // All of `cached_modules` (cached from previous incremental builds) can
+    // immediately go onto the `serialized_modules` modules list and then we can
+    // split the `modules` array into these two lists.
+    let mut in_memory = Vec::new();
+    serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| {
+        info!("pushing cached module {:?}", wp.cgu_name);
+        (buffer, CString::new(wp.cgu_name).unwrap())
+    }));
+    for module in modules {
+        match module {
+            FatLtoInput::InMemory(m) => in_memory.push(m),
+            FatLtoInput::Serialized { name, buffer } => {
+                info!("pushing serialized module {:?}", name);
+                let buffer = SerializedModule::Local(buffer);
+                serialized_modules.push((buffer, CString::new(name).unwrap()));
+            }
+        }
+    }
+
+    // Find the "costliest" module and merge everything into that codegen unit.
+    // All the other modules will be serialized and reparsed into the new
+    // context, so this hopefully avoids serializing and parsing the largest
+    // codegen unit.
+    //
+    // Additionally use a regular module as the base here to ensure that various
+    // file copy operations in the backend work correctly. The only other kind
+    // of module here should be an allocator one, and if your crate is smaller
+    // than the allocator module then the size doesn't really matter anyway.
+    let costliest_module = in_memory
+        .iter()
+        .enumerate()
+        .filter(|&(_, module)| module.kind == ModuleKind::Regular)
+        .map(|(i, _module)| {
+            //let cost = unsafe { llvm::LLVMRustModuleCost(module.module_llvm.llmod()) };
+            // TODO(antoyo): compute the cost of a module if GCC allows this.
+            (0, i)
+        })
+        .max();
+
+    // If we found a costliest module, we're good to go. Otherwise all our
+    // inputs were serialized which could happen in the case, for example, that
+    // all our inputs were incrementally reread from the cache and we're just
+    // re-executing the LTO passes. If that's the case deserialize the first
+    // module and create a linker with it.
+    let mut module: ModuleCodegen<GccContext> = match costliest_module {
+        Some((_cost, i)) => in_memory.remove(i),
+        None => {
+            unimplemented!("Incremental");
+            /*assert!(!serialized_modules.is_empty(), "must have at least one serialized module");
+            let (buffer, name) = serialized_modules.remove(0);
+            info!("no in-memory regular modules to choose from, parsing {:?}", name);
+            ModuleCodegen {
+                module_llvm: GccContext::parse(cgcx, &name, buffer.data(), diag_handler)?,
+                name: name.into_string().unwrap(),
+                kind: ModuleKind::Regular,
+            }*/
+        }
+    };
+    let mut serialized_bitcode = Vec::new();
+    {
+        info!("using {:?} as a base module", module.name);
+
+        // We cannot load and merge GCC contexts in memory like cg_llvm is doing.
+        // Instead, we combine the object files into a single object file.
+        for module in in_memory {
+            let path = tmp_path.path().to_path_buf().join(&module.name);
+            let path = path.to_str().expect("path");
+            let context = &module.module_llvm.context;
+            let config = cgcx.config(module.kind);
+            // NOTE: we need to set the optimization level here in order for LTO to do its job.
+            context.set_optimization_level(to_gcc_opt_level(config.opt_level));
+            context.add_command_line_option("-flto=auto");
+            context.add_command_line_option("-flto-partition=one");
+            context.compile_to_file(OutputKind::ObjectFile, path);
+            let buffer = ModuleBuffer::new(PathBuf::from(path));
+            let llmod_id = CString::new(&module.name[..]).unwrap();
+            serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
+        }
+        // Sort the modules to ensure we produce deterministic results.
+        serialized_modules.sort_by(|module1, module2| module1.1.cmp(&module2.1));
+
+        // We add the object files and save in should_combine_object_files that we should combine
+        // them into a single object file when compiling later.
+        for (bc_decoded, name) in serialized_modules {
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg_recorder("GCC_fat_lto_link_module", |recorder| {
+                    recorder.record_arg(format!("{:?}", name))
+                });
+            info!("linking {:?}", name);
+            match bc_decoded {
+                SerializedModule::Local(ref module_buffer) => {
+                    module.module_llvm.should_combine_object_files = true;
+                    module.module_llvm.context.add_driver_option(module_buffer.0.to_str().expect("path"));
+                },
+                SerializedModule::FromRlib(_) => unimplemented!("from rlib"),
+                SerializedModule::FromUncompressedFile(_) => unimplemented!("from uncompressed file"),
+            }
+            serialized_bitcode.push(bc_decoded);
+        }
+        save_temp_bitcode(cgcx, &module, "lto.input");
+
+        // Internalize everything below threshold to help strip out more modules and such.
+        /*unsafe {
+            let ptr = symbols_below_threshold.as_ptr();
+            llvm::LLVMRustRunRestrictionPass(
+                llmod,
+                ptr as *const *const libc::c_char,
+                symbols_below_threshold.len() as libc::size_t,
+            );*/
+            save_temp_bitcode(cgcx, &module, "lto.after-restriction");
+        //}
+    }
+
+    // NOTE: save the temporary directory used by LTO so that it gets deleted after linking instead
+    // of now.
+    module.module_llvm.temp_dir = Some(tmp_path);
+
+    Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode })
+}
+
+pub struct ModuleBuffer(PathBuf);
+
+impl ModuleBuffer {
+    pub fn new(path: PathBuf) -> ModuleBuffer {
+        ModuleBuffer(path)
+    }
+}
+
+impl ModuleBufferMethods for ModuleBuffer {
+    fn data(&self) -> &[u8] {
+        unimplemented!("data not needed for GCC codegen");
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/back/mod.rs b/compiler/rustc_codegen_gcc/src/back/mod.rs
index d692799d764..10187eab0d7 100644
--- a/compiler/rustc_codegen_gcc/src/back/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/back/mod.rs
@@ -1 +1,2 @@
+pub mod lto;
 pub mod write;
diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs
index 5f54ac4ebc6..04772d7707a 100644
--- a/compiler/rustc_codegen_gcc/src/back/write.rs
+++ b/compiler/rustc_codegen_gcc/src/back/write.rs
@@ -2,27 +2,71 @@ use std::{env, fs};
 
 use gccjit::OutputKind;
 use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
-use rustc_codegen_ssa::back::write::{CodegenContext, EmitObj, ModuleConfig};
+use rustc_codegen_ssa::back::link::ensure_removed;
+use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
 use rustc_errors::Handler;
+use rustc_fs_util::link_or_copy;
 use rustc_session::config::OutputType;
 use rustc_span::fatal_error::FatalError;
 use rustc_target::spec::SplitDebuginfo;
 
 use crate::{GccCodegenBackend, GccContext};
+use crate::errors::CopyBitcode;
 
-pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, module: ModuleCodegen<GccContext>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
-    let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name);
+pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, diag_handler: &Handler, module: ModuleCodegen<GccContext>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
+    let _timer = cgcx.prof.generic_activity_with_arg("GCC_module_codegen", &*module.name);
     {
         let context = &module.module_llvm.context;
 
         let module_name = module.name.clone();
+
+        let should_combine_object_files = module.module_llvm.should_combine_object_files;
+
         let module_name = Some(&module_name[..]);
 
-        let _bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
+        // NOTE: Only generate object files with GIMPLE when this environment variable is set for
+        // now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit).
+        let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1");
+
+        let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
         let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
 
-        if config.bitcode_needed() {
+        if config.bitcode_needed() && fat_lto {
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name);
+
             // TODO(antoyo)
+            /*if let Some(bitcode_filename) = bc_out.file_name() {
+                cgcx.prof.artifact_size(
+                    "llvm_bitcode",
+                    bitcode_filename.to_string_lossy(),
+                    data.len() as u64,
+                );
+            }*/
+
+            if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
+                let _timer = cgcx
+                    .prof
+                    .generic_activity_with_arg("GCC_module_codegen_emit_bitcode", &*module.name);
+                context.add_command_line_option("-flto=auto");
+                context.add_command_line_option("-flto-partition=one");
+                context.compile_to_file(OutputKind::ObjectFile, bc_out.to_str().expect("path to str"));
+            }
+
+            if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) {
+                let _timer = cgcx
+                    .prof
+                    .generic_activity_with_arg("GCC_module_codegen_embed_bitcode", &*module.name);
+                // TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes?
+                //embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data);
+
+                context.add_command_line_option("-flto=auto");
+                context.add_command_line_option("-flto-partition=one");
+                context.add_command_line_option("-ffat-lto-objects");
+                // TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument).
+                context.compile_to_file(OutputKind::ObjectFile, bc_out.to_str().expect("path to str"));
+            }
         }
 
         if config.emit_ir {
@@ -32,7 +76,7 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
         if config.emit_asm {
             let _timer = cgcx
                 .prof
-                .generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);
+                .generic_activity_with_arg("GCC_module_codegen_emit_asm", &*module.name);
             let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
             context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
         }
@@ -41,7 +85,7 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
             EmitObj::ObjectCode(_) => {
                 let _timer = cgcx
                     .prof
-                    .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &*module.name);
+                    .generic_activity_with_arg("GCC_module_codegen_emit_obj", &*module.name);
                 if env::var("CG_GCCJIT_DUMP_MODULE_NAMES").as_deref() == Ok("1") {
                     println!("Module {}", module.name);
                 }
@@ -60,11 +104,36 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
                     context.set_debug_info(true);
                     context.dump_to_file(path, true);
                 }
-                context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str"));
+                if should_combine_object_files && fat_lto {
+                    context.add_command_line_option("-flto=auto");
+                    context.add_command_line_option("-flto-partition=one");
+
+                    context.add_driver_option("-Wl,-r");
+                    // NOTE: we need -nostdlib, otherwise, we get the following error:
+                    // /usr/bin/ld: cannot find -lgcc_s: No such file or directory
+                    context.add_driver_option("-nostdlib");
+                    // NOTE: without -fuse-linker-plugin, we get the following error:
+                    // lto1: internal compiler error: decompressed stream: Destination buffer is too small
+                    context.add_driver_option("-fuse-linker-plugin");
+
+                    // NOTE: this doesn't actually generate an executable. With the above flags, it combines the .o files together in another .o.
+                    context.compile_to_file(OutputKind::Executable, obj_out.to_str().expect("path to str"));
+                }
+                else {
+                    context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str"));
+                }
             }
 
             EmitObj::Bitcode => {
-                // TODO(antoyo)
+                debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out);
+                if let Err(err) = link_or_copy(&bc_out, &obj_out) {
+                    diag_handler.emit_err(CopyBitcode { err });
+                }
+
+                if !config.emit_bc {
+                    debug!("removing_bitcode {:?}", bc_out);
+                    ensure_removed(diag_handler, &bc_out);
+                }
             }
 
             EmitObj::None => {}
@@ -82,3 +151,18 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
 pub(crate) fn link(_cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, mut _modules: Vec<ModuleCodegen<GccContext>>) -> Result<ModuleCodegen<GccContext>, FatalError> {
     unimplemented!();
 }
+
+pub(crate) fn save_temp_bitcode(cgcx: &CodegenContext<GccCodegenBackend>, _module: &ModuleCodegen<GccContext>, _name: &str) {
+    if !cgcx.save_temps {
+        return;
+    }
+    unimplemented!();
+    /*unsafe {
+        let ext = format!("{}.bc", name);
+        let cgu = Some(&module.name[..]);
+        let path = cgcx.output_filenames.temp_path_ext(&ext, cgu);
+        let cstr = path_to_c_string(&path);
+        let llmod = module.module_llvm.llmod();
+        llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
+    }*/
+}
diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs
index 9e614ca4ace..b081e9ff2fd 100644
--- a/compiler/rustc_codegen_gcc/src/base.rs
+++ b/compiler/rustc_codegen_gcc/src/base.rs
@@ -1,3 +1,4 @@
+use std::collections::HashSet;
 use std::env;
 use std::time::Instant;
 
@@ -18,6 +19,7 @@ use rustc_codegen_ssa::traits::DebugInfoMethods;
 use rustc_session::config::DebugInfo;
 use rustc_span::Symbol;
 
+use crate::{LockedTargetInfo, gcc_util};
 use crate::GccContext;
 use crate::builder::Builder;
 use crate::context::CodegenCx;
@@ -50,6 +52,7 @@ pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind {
 pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType {
     match linkage {
         Linkage::External => FunctionType::Exported,
+        // TODO(antoyo): set the attribute externally_visible.
         Linkage::AvailableExternally => FunctionType::Extern,
         Linkage::LinkOnceAny => unimplemented!(),
         Linkage::LinkOnceODR => unimplemented!(),
@@ -63,7 +66,7 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType {
     }
 }
 
-pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_integers: bool) -> (ModuleCodegen<GccContext>, u64) {
+pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: LockedTargetInfo) -> (ModuleCodegen<GccContext>, u64) {
     let prof_timer = tcx.prof.generic_activity("codegen_module");
     let start_time = Instant::now();
 
@@ -71,7 +74,7 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_i
     let (module, _) = tcx.dep_graph.with_task(
         dep_node,
         tcx,
-        (cgu_name, supports_128bit_integers),
+        (cgu_name, target_info),
         module_codegen,
         Some(dep_graph::hash_result),
     );
@@ -82,38 +85,28 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_i
     // the time we needed for codegenning it.
     let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;
 
-    fn module_codegen(tcx: TyCtxt<'_>, (cgu_name, supports_128bit_integers): (Symbol, bool)) -> ModuleCodegen<GccContext> {
+    fn module_codegen(tcx: TyCtxt<'_>, (cgu_name, target_info): (Symbol, LockedTargetInfo)) -> ModuleCodegen<GccContext> {
         let cgu = tcx.codegen_unit(cgu_name);
         // Instantiate monomorphizations without filling out definitions yet...
-        //let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str());
         let context = Context::default();
 
         context.add_command_line_option("-fexceptions");
         context.add_driver_option("-fexceptions");
 
+        let disabled_features: HashSet<_> = tcx.sess.opts.cg.target_feature.split(',')
+            .filter(|feature| feature.starts_with('-'))
+            .map(|string| &string[1..])
+            .collect();
+
         // TODO(antoyo): only set on x86 platforms.
         context.add_command_line_option("-masm=intel");
-        // TODO(antoyo): only add the following cli argument if the feature is supported.
-        context.add_command_line_option("-msse2");
-        context.add_command_line_option("-mavx2");
-        // FIXME(antoyo): the following causes an illegal instruction on vmovdqu64 in std_example on my CPU.
-        // Only add if the CPU supports it.
-        context.add_command_line_option("-msha");
-        context.add_command_line_option("-mpclmul");
-        context.add_command_line_option("-mfma");
-        context.add_command_line_option("-mfma4");
-        context.add_command_line_option("-m64");
-        context.add_command_line_option("-mbmi");
-        context.add_command_line_option("-mgfni");
-        //context.add_command_line_option("-mavxvnni"); // The CI doesn't support this option.
-        context.add_command_line_option("-mf16c");
-        context.add_command_line_option("-maes");
-        context.add_command_line_option("-mxsavec");
-        context.add_command_line_option("-mbmi2");
-        context.add_command_line_option("-mrtm");
-        context.add_command_line_option("-mvaes");
-        context.add_command_line_option("-mvpclmulqdq");
-        context.add_command_line_option("-mavx");
+
+        if !disabled_features.contains("avx") {
+            // NOTE: we always enable AVX because the equivalent of llvm.x86.sse2.cmp.pd in GCC for
+            // SSE2 is multiple builtins, so we use the AVX __builtin_ia32_cmppd instead.
+            // FIXME(antoyo): use the proper builtins for llvm.x86.sse2.cmp.pd and similar.
+            context.add_command_line_option("-mavx");
+        }
 
         for arg in &tcx.sess.opts.cg.llvm_args {
             context.add_command_line_option(arg);
@@ -127,6 +120,16 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_i
         // NOTE: Rust relies on LLVM doing wrapping on overflow.
         context.add_command_line_option("-fwrapv");
 
+        if tcx.sess.relocation_model() == rustc_target::spec::RelocModel::Static {
+            context.add_command_line_option("-mcmodel=kernel");
+            context.add_command_line_option("-fno-pie");
+        }
+
+        let target_cpu = gcc_util::target_cpu(tcx.sess);
+        if target_cpu != "generic" {
+            context.add_command_line_option(&format!("-march={}", target_cpu));
+        }
+
         if tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections) {
             context.add_command_line_option("-ffunction-sections");
             context.add_command_line_option("-fdata-sections");
@@ -135,8 +138,14 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_i
         if env::var("CG_GCCJIT_DUMP_RTL").as_deref() == Ok("1") {
             context.add_command_line_option("-fdump-rtl-vregs");
         }
+        if env::var("CG_GCCJIT_DUMP_RTL_ALL").as_deref() == Ok("1") {
+            context.add_command_line_option("-fdump-rtl-all");
+        }
         if env::var("CG_GCCJIT_DUMP_TREE_ALL").as_deref() == Ok("1") {
-            context.add_command_line_option("-fdump-tree-all");
+            context.add_command_line_option("-fdump-tree-all-eh");
+        }
+        if env::var("CG_GCCJIT_DUMP_IPA_ALL").as_deref() == Ok("1") {
+            context.add_command_line_option("-fdump-ipa-all-eh");
         }
         if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") {
             context.set_dump_code_on_compile(true);
@@ -152,11 +161,15 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_i
             context.set_keep_intermediates(true);
         }
 
+        if env::var("CG_GCCJIT_VERBOSE").as_deref() == Ok("1") {
+            context.add_driver_option("-v");
+        }
+
         // NOTE: The codegen generates unrechable blocks.
         context.set_allow_unreachable_blocks(true);
 
         {
-            let cx = CodegenCx::new(&context, cgu, tcx, supports_128bit_integers);
+            let cx = CodegenCx::new(&context, cgu, tcx, target_info.supports_128bit_int());
 
             let mono_items = cgu.items_in_deterministic_order(tcx);
             for &(mono_item, data) in &mono_items {
@@ -181,7 +194,9 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_i
         ModuleCodegen {
             name: cgu_name.to_string(),
             module_llvm: GccContext {
-                context
+                context,
+                should_combine_object_files: false,
+                temp_dir: None,
             },
             kind: ModuleKind::Regular,
         }
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index ecc293aee23..b7841808934 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -247,16 +247,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
     }
 
     fn check_store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> {
-        let dest_ptr_ty = self.cx.val_ty(ptr).make_pointer(); // TODO(antoyo): make sure make_pointer() is okay here.
         let stored_ty = self.cx.val_ty(val);
         let stored_ptr_ty = self.cx.type_ptr_to(stored_ty);
-
-        if dest_ptr_ty == stored_ptr_ty {
-            ptr
-        }
-        else {
-            self.bitcast(ptr, stored_ptr_ty)
-        }
+        self.bitcast(ptr, stored_ptr_ty)
     }
 
     pub fn current_func(&self) -> Function<'gcc> {
@@ -500,7 +493,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
     }
 
     #[cfg(not(feature="master"))]
-    fn invoke(&mut self, typ: Type<'gcc>, fn_attrs: &CodegenFnAttrs, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
+    fn invoke(&mut self, typ: Type<'gcc>, fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
         let call_site = self.call(typ, fn_attrs, None, func, args, None);
         let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
         self.llbb().end_with_conditional(None, condition, then, catch);
@@ -663,7 +656,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
     }
 
     fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
-        a + b
+        self.gcc_add(a, b)
     }
 
     fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
@@ -671,7 +664,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
     }
 
     fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
-        a - b
+        self.gcc_sub(a, b)
     }
 
     fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
@@ -680,11 +673,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
     }
 
     fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
-        a * b
+        self.gcc_mul(a, b)
     }
 
     fn unchecked_umul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
-        a * b
+        self.gcc_mul(a, b)
     }
 
     fn fadd_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
@@ -916,7 +909,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
             .add_eval(None, self.context.new_call(None, atomic_store, &[ptr, value, ordering]));
     }
 
-    fn gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+    fn gep(&mut self, typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+        // NOTE: due to opaque pointers now being used, we need to cast here.
+        let ptr = self.context.new_cast(None, ptr, typ.make_pointer());
         let ptr_type = ptr.get_type();
         let mut pointee_type = ptr.get_type();
         // NOTE: we cannot use array indexing here like in inbounds_gep because array indexing is
@@ -927,6 +922,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         // require dereferencing the pointer.
         for index in indices {
             pointee_type = pointee_type.get_pointee().expect("pointee type");
+            #[cfg(feature="master")]
+            let pointee_size = {
+                let size = self.cx.context.new_sizeof(pointee_type);
+                self.context.new_cast(None, size, index.get_type())
+            };
+            #[cfg(not(feature="master"))]
             let pointee_size = self.context.new_rvalue_from_int(index.get_type(), pointee_type.get_size() as i32);
             result = result + self.gcc_int_cast(*index * pointee_size, self.sizet_type);
         }
diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs
index 493626c3cf5..e673d0af4c7 100644
--- a/compiler/rustc_codegen_gcc/src/declare.rs
+++ b/compiler/rustc_codegen_gcc/src/declare.rs
@@ -1,4 +1,6 @@
 use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type};
+#[cfg(feature="master")]
+use gccjit::{FnAttribute, ToRValue};
 use rustc_codegen_ssa::traits::BaseTypeMethods;
 use rustc_middle::ty::Ty;
 use rustc_span::Symbol;
@@ -114,6 +116,44 @@ fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*ll
                 .collect();
             let func = cx.context.new_function(None, cx.linkage.get(), return_type, &params, mangle_name(name), variadic);
             cx.functions.borrow_mut().insert(name.to_string(), func);
+
+            #[cfg(feature="master")]
+            if name == "rust_eh_personality" {
+                // NOTE: GCC will sometimes change the personality function set on a function from
+                // rust_eh_personality to __gcc_personality_v0 as an optimization.
+                // As such, we need to create a weak alias from __gcc_personality_v0 to
+                // rust_eh_personality in order to avoid a linker error.
+                // This needs to be weak in order to still allow using the standard
+                // __gcc_personality_v0 when the linking to it.
+                // Since aliases don't work (maybe because of a bug in LTO partitioning?), we
+                // create a wrapper function that calls rust_eh_personality.
+
+                let params: Vec<_> = param_types.into_iter().enumerate()
+                    .map(|(index, param)| cx.context.new_parameter(None, *param, &format!("param{}", index))) // TODO(antoyo): set name.
+                    .collect();
+                let gcc_func = cx.context.new_function(None, FunctionType::Exported, return_type, &params, "__gcc_personality_v0", variadic);
+
+                // We need a normal extern function for the crates that access rust_eh_personality
+                // without defining it, otherwise we'll get a compiler error.
+                //
+                // For the crate defining it, that needs to be a weak alias instead.
+                gcc_func.add_attribute(FnAttribute::Weak);
+
+                let block = gcc_func.new_block("start");
+                let mut args = vec![];
+                for param in &params {
+                    args.push(param.to_rvalue());
+                }
+                let call = cx.context.new_call(None, func, &args);
+                if return_type == cx.type_void() {
+                    block.add_eval(None, call);
+                    block.end_with_void_return(None);
+                }
+                else {
+                    block.end_with_return(None, call);
+                }
+            }
+
             func
         };
 
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 693367192b1..4bf3b71f503 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,8 +1,36 @@
-use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
-use rustc_macros::Diagnostic;
+use rustc_errors::{
+    DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, IntoDiagnosticArg,
+};
+use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
 use std::borrow::Cow;
 
+use crate::fluent_generated as fluent;
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
+#[note]
+pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
+    pub feature: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_unknown_ctarget_feature)]
+#[note]
+pub(crate) struct UnknownCTargetFeature<'a> {
+    pub feature: &'a str,
+    #[subdiagnostic]
+    pub rust_feature: PossibleFeature<'a>,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum PossibleFeature<'a> {
+    #[help(codegen_gcc_possible_feature)]
+    Some { rust_feature: &'a str },
+    #[help(codegen_gcc_consider_filing_feature_request)]
+    None,
+}
+
 struct ExitCode(Option<i32>);
 
 impl IntoDiagnosticArg for ExitCode {
@@ -40,3 +68,58 @@ pub(crate) struct TiedTargetFeatures {
     pub span: Span,
     pub features: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_copy_bitcode)]
+pub(crate) struct CopyBitcode {
+    pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_dynamic_linking_with_lto)]
+#[note]
+pub(crate) struct DynamicLinkingWithLTO;
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_load_bitcode)]
+pub(crate) struct LoadBitcode {
+    name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_lto_disallowed)]
+pub(crate) struct LtoDisallowed;
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_lto_dylib)]
+pub(crate) struct LtoDylib;
+
+#[derive(Diagnostic)]
+#[diag(codegen_gcc_lto_bitcode_from_rlib)]
+pub(crate) struct LtoBitcodeFromRlib {
+    pub gcc_err: String,
+}
+
+pub(crate) struct TargetFeatureDisableOrEnable<'a> {
+    pub features: &'a [&'a str],
+    pub span: Option<Span>,
+    pub missing_features: Option<MissingFeatures>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(codegen_gcc_missing_features)]
+pub(crate) struct MissingFeatures;
+
+impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
+    fn into_diagnostic(self, sess: &'_ Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = sess.struct_err(fluent::codegen_gcc_target_feature_disable_or_enable);
+        if let Some(span) = self.span {
+            diag.set_span(span);
+        };
+        if let Some(missing_features) = self.missing_features {
+            diag.subdiagnostic(missing_features);
+        }
+        diag.set_arg("features", self.features.join(", "));
+        diag
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
new file mode 100644
index 00000000000..0514c9988e0
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -0,0 +1,223 @@
+#[cfg(feature="master")]
+use gccjit::Context;
+use smallvec::{smallvec, SmallVec};
+
+use rustc_codegen_ssa::target_features::{
+    supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES,
+};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::bug;
+use rustc_session::Session;
+
+use crate::errors::{PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
+
+/// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
+/// `--target` and similar).
+pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<String> {
+    // Features that come earlier are overridden by conflicting features later in the string.
+    // Typically we'll want more explicit settings to override the implicit ones, so:
+    //
+    // * Features from -Ctarget-cpu=*; are overridden by [^1]
+    // * Features implied by --target; are overridden by
+    // * Features from -Ctarget-feature; are overridden by
+    // * function specific features.
+    //
+    // [^1]: target-cpu=native is handled here, other target-cpu values are handled implicitly
+    // through GCC march implementation.
+    //
+    // FIXME(nagisa): it isn't clear what's the best interaction between features implied by
+    // `-Ctarget-cpu` and `--target` are. On one hand, you'd expect CLI arguments to always
+    // override anything that's implicit, so e.g. when there's no `--target` flag, features implied
+    // the host target are overridden by `-Ctarget-cpu=*`. On the other hand, what about when both
+    // `--target` and `-Ctarget-cpu=*` are specified? Both then imply some target features and both
+    // flags are specified by the user on the CLI. It isn't as clear-cut which order of precedence
+    // should be taken in cases like these.
+    let mut features = vec![];
+
+    // Features implied by an implicit or explicit `--target`.
+    features.extend(
+        sess.target
+            .features
+            .split(',')
+            .filter(|v| !v.is_empty() && backend_feature_name(v).is_some())
+            .map(String::from),
+    );
+
+    // -Ctarget-features
+    let supported_features = supported_target_features(sess);
+    let mut featsmap = FxHashMap::default();
+    let feats = sess.opts.cg.target_feature
+        .split(',')
+        .filter_map(|s| {
+            let enable_disable = match s.chars().next() {
+                None => return None,
+                Some(c @ ('+' | '-')) => c,
+                Some(_) => {
+                    if diagnostics {
+                        sess.emit_warning(UnknownCTargetFeaturePrefix { feature: s });
+                    }
+                    return None;
+                }
+            };
+
+            let feature = backend_feature_name(s)?;
+            // Warn against use of GCC specific feature names on the CLI.
+            if diagnostics && !supported_features.iter().any(|&(v, _)| v == feature) {
+                let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| {
+                    let gcc_features = to_gcc_features(sess, rust_feature);
+                    if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) {
+                        Some(rust_feature)
+                    } else {
+                        None
+                    }
+                });
+                let unknown_feature =
+                    if let Some(rust_feature) = rust_feature {
+                        UnknownCTargetFeature {
+                            feature,
+                            rust_feature: PossibleFeature::Some { rust_feature },
+                        }
+                    }
+                    else {
+                        UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
+                    };
+                sess.emit_warning(unknown_feature);
+            }
+
+            if diagnostics {
+                // FIXME(nagisa): figure out how to not allocate a full hashset here.
+                featsmap.insert(feature, enable_disable == '+');
+            }
+
+            // rustc-specific features do not get passed down to GCC…
+            if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
+                return None;
+            }
+            // ... otherwise though we run through `to_gcc_features` when
+            // passing requests down to GCC. This means that all in-language
+            // features also work on the command line instead of having two
+            // different names when the GCC name and the Rust name differ.
+            Some(to_gcc_features(sess, feature)
+                .iter()
+                .flat_map(|feat| to_gcc_features(sess, feat).into_iter())
+                .map(|feature| {
+                    if enable_disable == '-' {
+                        format!("-{}", feature)
+                    }
+                    else {
+                        feature.to_string()
+                    }
+                })
+                .collect::<Vec<_>>(),
+            )
+        })
+        .flatten();
+    features.extend(feats);
+
+    if diagnostics {
+        if let Some(f) = check_tied_features(sess, &featsmap) {
+            sess.emit_err(TargetFeatureDisableOrEnable {
+                features: f,
+                span: None,
+                missing_features: None,
+            });
+        }
+    }
+
+    features
+}
+
+/// Returns a feature name for the given `+feature` or `-feature` string.
+///
+/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
+fn backend_feature_name(s: &str) -> Option<&str> {
+    // features must start with a `+` or `-`.
+    let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| {
+        bug!("target feature `{}` must begin with a `+` or `-`", s);
+    });
+    // Rustc-specific feature requests like `+crt-static` or `-crt-static`
+    // are not passed down to GCC.
+    if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
+        return None;
+    }
+    Some(feature)
+}
+
+// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
+pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
+    let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
+    match (arch, s) {
+        ("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
+        ("x86", "pclmulqdq") => smallvec!["pclmul"],
+        ("x86", "rdrand") => smallvec!["rdrnd"],
+        ("x86", "bmi1") => smallvec!["bmi"],
+        ("x86", "cmpxchg16b") => smallvec!["cx16"],
+        ("x86", "avx512vaes") => smallvec!["vaes"],
+        ("x86", "avx512gfni") => smallvec!["gfni"],
+        ("x86", "avx512vpclmulqdq") => smallvec!["vpclmulqdq"],
+        // NOTE: seems like GCC requires 'avx512bw' for 'avx512vbmi2'.
+        ("x86", "avx512vbmi2") => smallvec!["avx512vbmi2", "avx512bw"],
+        // NOTE: seems like GCC requires 'avx512bw' for 'avx512bitalg'.
+        ("x86", "avx512bitalg") => smallvec!["avx512bitalg", "avx512bw"],
+        ("aarch64", "rcpc2") => smallvec!["rcpc-immo"],
+        ("aarch64", "dpb") => smallvec!["ccpp"],
+        ("aarch64", "dpb2") => smallvec!["ccdp"],
+        ("aarch64", "frintts") => smallvec!["fptoint"],
+        ("aarch64", "fcma") => smallvec!["complxnum"],
+        ("aarch64", "pmuv3") => smallvec!["perfmon"],
+        ("aarch64", "paca") => smallvec!["pauth"],
+        ("aarch64", "pacg") => smallvec!["pauth"],
+        // Rust ties fp and neon together. In GCC neon implicitly enables fp,
+        // but we manually enable neon when a feature only implicitly enables fp
+        ("aarch64", "f32mm") => smallvec!["f32mm", "neon"],
+        ("aarch64", "f64mm") => smallvec!["f64mm", "neon"],
+        ("aarch64", "fhm") => smallvec!["fp16fml", "neon"],
+        ("aarch64", "fp16") => smallvec!["fullfp16", "neon"],
+        ("aarch64", "jsconv") => smallvec!["jsconv", "neon"],
+        ("aarch64", "sve") => smallvec!["sve", "neon"],
+        ("aarch64", "sve2") => smallvec!["sve2", "neon"],
+        ("aarch64", "sve2-aes") => smallvec!["sve2-aes", "neon"],
+        ("aarch64", "sve2-sm4") => smallvec!["sve2-sm4", "neon"],
+        ("aarch64", "sve2-sha3") => smallvec!["sve2-sha3", "neon"],
+        ("aarch64", "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
+        (_, s) => smallvec![s],
+    }
+}
+
+// Given a map from target_features to whether they are enabled or disabled,
+// ensure only valid combinations are allowed.
+pub fn check_tied_features(sess: &Session, features: &FxHashMap<&str, bool>) -> Option<&'static [&'static str]> {
+    for tied in tied_target_features(sess) {
+        // Tied features must be set to the same value, or not set at all
+        let mut tied_iter = tied.iter();
+        let enabled = features.get(tied_iter.next().unwrap());
+        if tied_iter.any(|feature| enabled != features.get(feature)) {
+            return Some(tied);
+        }
+    }
+    None
+}
+
+fn handle_native(name: &str) -> &str {
+    if name != "native" {
+        return name;
+    }
+
+    #[cfg(feature="master")]
+    {
+        // Get the native arch.
+        let context = Context::default();
+        context.get_target_info().arch().unwrap()
+            .to_str()
+            .unwrap()
+    }
+    #[cfg(not(feature="master"))]
+    unimplemented!();
+}
+
+pub fn target_cpu(sess: &Session) -> &str {
+    match sess.opts.cg.target_cpu {
+        Some(ref name) => handle_native(name),
+        None => handle_native(sess.target.cpu.as_ref()),
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs
index 0cf1204791d..58e0dd56f38 100644
--- a/compiler/rustc_codegen_gcc/src/int.rs
+++ b/compiler/rustc_codegen_gcc/src/int.rs
@@ -36,7 +36,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
             self.cx.context.new_unary_op(None, operation, typ, a)
         }
         else {
-            // TODO(antoyo): use __negdi2 and __negti2 instead?
             let element_type = typ.dyncast_array().expect("element type");
             let values = [
                 self.cx.context.new_unary_op(None, UnaryOp::BitwiseNegate, element_type, self.low(a)),
@@ -52,9 +51,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
             self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a)
         }
         else {
-            let param_a = self.context.new_parameter(None, a_type, "a");
-            let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a], "__negti2", false);
-            self.context.new_call(None, func, &[a])
+            self.gcc_add(self.gcc_not(a), self.gcc_int(a_type, 1))
         }
     }
 
@@ -353,23 +350,63 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
         (res.dereference(None).to_rvalue(), overflow)
     }
 
-    pub fn gcc_icmp(&self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
+    pub fn gcc_icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
         let a_type = lhs.get_type();
         let b_type = rhs.get_type();
         if self.is_non_native_int_type(a_type) || self.is_non_native_int_type(b_type) {
-            let signed = a_type.is_compatible_with(self.i128_type);
-            let sign =
-                if signed {
-                    ""
-                }
-                else {
-                    "u"
-                };
-            let func_name = format!("__{}cmpti2", sign);
-            let param_a = self.context.new_parameter(None, a_type, "a");
-            let param_b = self.context.new_parameter(None, b_type, "b");
-            let func = self.context.new_function(None, FunctionType::Extern, self.int_type, &[param_a, param_b], func_name, false);
-            let cmp = self.context.new_call(None, func, &[lhs, rhs]);
+            // This algorithm is based on compiler-rt's __cmpti2:
+            // https://github.com/llvm-mirror/compiler-rt/blob/f0745e8476f069296a7c71accedd061dce4cdf79/lib/builtins/cmpti2.c#L21
+            let result = self.current_func().new_local(None, self.int_type, "icmp_result");
+            let block1 = self.current_func().new_block("block1");
+            let block2 = self.current_func().new_block("block2");
+            let block3 = self.current_func().new_block("block3");
+            let block4 = self.current_func().new_block("block4");
+            let block5 = self.current_func().new_block("block5");
+            let block6 = self.current_func().new_block("block6");
+            let block7 = self.current_func().new_block("block7");
+            let block8 = self.current_func().new_block("block8");
+            let after = self.current_func().new_block("after");
+
+            let native_int_type = a_type.dyncast_array().expect("get element type");
+            // NOTE: cast low to its unsigned type in order to perform a comparison correctly (e.g.
+            // the sign is only on high).
+            let unsigned_type = native_int_type.to_unsigned(&self.cx);
+
+            let lhs_low = self.context.new_cast(None, self.low(lhs), unsigned_type);
+            let rhs_low = self.context.new_cast(None, self.low(rhs), unsigned_type);
+
+            let condition = self.context.new_comparison(None, ComparisonOp::LessThan, self.high(lhs), self.high(rhs));
+            self.llbb().end_with_conditional(None, condition, block1, block2);
+
+            block1.add_assignment(None, result, self.context.new_rvalue_zero(self.int_type));
+            block1.end_with_jump(None, after);
+
+            let condition = self.context.new_comparison(None, ComparisonOp::GreaterThan, self.high(lhs), self.high(rhs));
+            block2.end_with_conditional(None, condition, block3, block4);
+
+            block3.add_assignment(None, result, self.context.new_rvalue_from_int(self.int_type, 2));
+            block3.end_with_jump(None, after);
+
+            let condition = self.context.new_comparison(None, ComparisonOp::LessThan, lhs_low, rhs_low);
+            block4.end_with_conditional(None, condition, block5, block6);
+
+            block5.add_assignment(None, result, self.context.new_rvalue_zero(self.int_type));
+            block5.end_with_jump(None, after);
+
+            let condition = self.context.new_comparison(None, ComparisonOp::GreaterThan, lhs_low, rhs_low);
+            block6.end_with_conditional(None, condition, block7, block8);
+
+            block7.add_assignment(None, result, self.context.new_rvalue_from_int(self.int_type, 2));
+            block7.end_with_jump(None, after);
+
+            block8.add_assignment(None, result, self.context.new_rvalue_one(self.int_type));
+            block8.end_with_jump(None, after);
+
+            // NOTE: since jumps were added in a place rustc does not expect, the current block in the
+            // state need to be updated.
+            self.switch_to_block(after);
+
+            let cmp = result.to_rvalue();
             let (op, limit) =
                 match op {
                     IntPredicate::IntEQ => {
@@ -546,7 +583,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
     }
 
     pub fn gcc_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> {
-        if self.is_native_int_type_or_bool(typ) {
+        if typ.is_u128(self) {
+            // FIXME(antoyo): libgccjit cannot create 128-bit values yet.
+            let num = self.context.new_rvalue_from_long(self.u64_type, int as i64);
+            self.gcc_int_cast(num, typ)
+        }
+        else if self.is_native_int_type_or_bool(typ) {
             self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64)
         }
         else {
@@ -572,6 +614,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
             }
         }
         else if typ.is_i128(self) {
+            // FIXME(antoyo): libgccjit cannot create 128-bit values yet.
             let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
             self.gcc_int_cast(num, typ)
         }
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
index 438eab78943..e01299d32fd 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
@@ -2254,6 +2254,42 @@ match name {
     "llvm.hexagon.prefetch" => "__builtin_HEXAGON_prefetch",
     "llvm.hexagon.vmemcpy" => "__builtin_hexagon_vmemcpy",
     "llvm.hexagon.vmemset" => "__builtin_hexagon_vmemset",
+    // loongarch
+    "llvm.loongarch.asrtgt.d" => "__builtin_loongarch_asrtgt_d",
+    "llvm.loongarch.asrtle.d" => "__builtin_loongarch_asrtle_d",
+    "llvm.loongarch.break" => "__builtin_loongarch_break",
+    "llvm.loongarch.cacop.d" => "__builtin_loongarch_cacop_d",
+    "llvm.loongarch.cacop.w" => "__builtin_loongarch_cacop_w",
+    "llvm.loongarch.cpucfg" => "__builtin_loongarch_cpucfg",
+    "llvm.loongarch.crc.w.b.w" => "__builtin_loongarch_crc_w_b_w",
+    "llvm.loongarch.crc.w.d.w" => "__builtin_loongarch_crc_w_d_w",
+    "llvm.loongarch.crc.w.h.w" => "__builtin_loongarch_crc_w_h_w",
+    "llvm.loongarch.crc.w.w.w" => "__builtin_loongarch_crc_w_w_w",
+    "llvm.loongarch.crcc.w.b.w" => "__builtin_loongarch_crcc_w_b_w",
+    "llvm.loongarch.crcc.w.d.w" => "__builtin_loongarch_crcc_w_d_w",
+    "llvm.loongarch.crcc.w.h.w" => "__builtin_loongarch_crcc_w_h_w",
+    "llvm.loongarch.crcc.w.w.w" => "__builtin_loongarch_crcc_w_w_w",
+    "llvm.loongarch.csrrd.d" => "__builtin_loongarch_csrrd_d",
+    "llvm.loongarch.csrrd.w" => "__builtin_loongarch_csrrd_w",
+    "llvm.loongarch.csrwr.d" => "__builtin_loongarch_csrwr_d",
+    "llvm.loongarch.csrwr.w" => "__builtin_loongarch_csrwr_w",
+    "llvm.loongarch.csrxchg.d" => "__builtin_loongarch_csrxchg_d",
+    "llvm.loongarch.csrxchg.w" => "__builtin_loongarch_csrxchg_w",
+    "llvm.loongarch.dbar" => "__builtin_loongarch_dbar",
+    "llvm.loongarch.ibar" => "__builtin_loongarch_ibar",
+    "llvm.loongarch.iocsrrd.b" => "__builtin_loongarch_iocsrrd_b",
+    "llvm.loongarch.iocsrrd.d" => "__builtin_loongarch_iocsrrd_d",
+    "llvm.loongarch.iocsrrd.h" => "__builtin_loongarch_iocsrrd_h",
+    "llvm.loongarch.iocsrrd.w" => "__builtin_loongarch_iocsrrd_w",
+    "llvm.loongarch.iocsrwr.b" => "__builtin_loongarch_iocsrwr_b",
+    "llvm.loongarch.iocsrwr.d" => "__builtin_loongarch_iocsrwr_d",
+    "llvm.loongarch.iocsrwr.h" => "__builtin_loongarch_iocsrwr_h",
+    "llvm.loongarch.iocsrwr.w" => "__builtin_loongarch_iocsrwr_w",
+    "llvm.loongarch.lddir.d" => "__builtin_loongarch_lddir_d",
+    "llvm.loongarch.ldpte.d" => "__builtin_loongarch_ldpte_d",
+    "llvm.loongarch.movfcsr2gr" => "__builtin_loongarch_movfcsr2gr",
+    "llvm.loongarch.movgr2fcsr" => "__builtin_loongarch_movgr2fcsr",
+    "llvm.loongarch.syscall" => "__builtin_loongarch_syscall",
     // mips
     "llvm.mips.absq.s.ph" => "__builtin_mips_absq_s_ph",
     "llvm.mips.absq.s.qb" => "__builtin_mips_absq_s_qb",
@@ -2954,6 +2990,8 @@ match name {
     "llvm.nvvm.barrier0.and" => "__nvvm_bar0_and",
     "llvm.nvvm.barrier0.or" => "__nvvm_bar0_or",
     "llvm.nvvm.barrier0.popc" => "__nvvm_bar0_popc",
+    "llvm.nvvm.bf2h.rn" => "__nvvm_bf2h_rn",
+    "llvm.nvvm.bf2h.rn.ftz" => "__nvvm_bf2h_rn_ftz",
     "llvm.nvvm.bitcast.d2ll" => "__nvvm_bitcast_d2ll",
     "llvm.nvvm.bitcast.f2i" => "__nvvm_bitcast_f2i",
     "llvm.nvvm.bitcast.i2f" => "__nvvm_bitcast_i2f",
@@ -3016,8 +3054,6 @@ match name {
     "llvm.nvvm.div.rz.ftz.f" => "__nvvm_div_rz_ftz_f",
     "llvm.nvvm.ex2.approx.d" => "__nvvm_ex2_approx_d",
     "llvm.nvvm.ex2.approx.f" => "__nvvm_ex2_approx_f",
-    "llvm.nvvm.ex2.approx.f16" => "__nvvm_ex2_approx_f16",
-    "llvm.nvvm.ex2.approx.f16x2" => "__nvvm_ex2_approx_f16x2",
     "llvm.nvvm.ex2.approx.ftz.f" => "__nvvm_ex2_approx_ftz_f",
     "llvm.nvvm.f2bf16.rn" => "__nvvm_f2bf16_rn",
     "llvm.nvvm.f2bf16.rn.relu" => "__nvvm_f2bf16_rn_relu",
@@ -3079,11 +3115,17 @@ match name {
     "llvm.nvvm.fma.rn.bf16x2" => "__nvvm_fma_rn_bf16x2",
     "llvm.nvvm.fma.rn.d" => "__nvvm_fma_rn_d",
     "llvm.nvvm.fma.rn.f" => "__nvvm_fma_rn_f",
-    "llvm.nvvm.fma.rn.f16" => "__nvvm_fma_rn_f16",
-    "llvm.nvvm.fma.rn.f16x2" => "__nvvm_fma_rn_f16x2",
+    "llvm.nvvm.fma.rn.ftz.bf16" => "__nvvm_fma_rn_ftz_bf16",
+    "llvm.nvvm.fma.rn.ftz.bf16x2" => "__nvvm_fma_rn_ftz_bf16x2",
     "llvm.nvvm.fma.rn.ftz.f" => "__nvvm_fma_rn_ftz_f",
+    "llvm.nvvm.fma.rn.ftz.relu.bf16" => "__nvvm_fma_rn_ftz_relu_bf16",
+    "llvm.nvvm.fma.rn.ftz.relu.bf16x2" => "__nvvm_fma_rn_ftz_relu_bf16x2",
+    "llvm.nvvm.fma.rn.ftz.sat.bf16" => "__nvvm_fma_rn_ftz_sat_bf16",
+    "llvm.nvvm.fma.rn.ftz.sat.bf16x2" => "__nvvm_fma_rn_ftz_sat_bf16x2",
     "llvm.nvvm.fma.rn.relu.bf16" => "__nvvm_fma_rn_relu_bf16",
     "llvm.nvvm.fma.rn.relu.bf16x2" => "__nvvm_fma_rn_relu_bf16x2",
+    "llvm.nvvm.fma.rn.sat.bf16" => "__nvvm_fma_rn_sat_bf16",
+    "llvm.nvvm.fma.rn.sat.bf16x2" => "__nvvm_fma_rn_sat_bf16x2",
     "llvm.nvvm.fma.rp.d" => "__nvvm_fma_rp_d",
     "llvm.nvvm.fma.rp.f" => "__nvvm_fma_rp_f",
     "llvm.nvvm.fma.rp.ftz.f" => "__nvvm_fma_rp_ftz_f",
@@ -3094,11 +3136,17 @@ match name {
     "llvm.nvvm.fmax.bf16x2" => "__nvvm_fmax_bf16x2",
     "llvm.nvvm.fmax.d" => "__nvvm_fmax_d",
     "llvm.nvvm.fmax.f" => "__nvvm_fmax_f",
-    "llvm.nvvm.fmax.f16" => "__nvvm_fmax_f16",
-    "llvm.nvvm.fmax.f16x2" => "__nvvm_fmax_f16x2",
+    "llvm.nvvm.fmax.ftz.bf16" => "__nvvm_fmax_ftz_bf16",
+    "llvm.nvvm.fmax.ftz.bf16x2" => "__nvvm_fmax_ftz_bf16x2",
     "llvm.nvvm.fmax.ftz.f" => "__nvvm_fmax_ftz_f",
+    "llvm.nvvm.fmax.ftz.nan.bf16" => "__nvvm_fmax_ftz_nan_bf16",
+    "llvm.nvvm.fmax.ftz.nan.bf16x2" => "__nvvm_fmax_ftz_nan_bf16x2",
     "llvm.nvvm.fmax.ftz.nan.f" => "__nvvm_fmax_ftz_nan_f",
+    "llvm.nvvm.fmax.ftz.nan.xorsign.abs.bf16" => "__nvvm_fmax_ftz_nan_xorsign_abs_bf16",
+    "llvm.nvvm.fmax.ftz.nan.xorsign.abs.bf16x2" => "__nvvm_fmax_ftz_nan_xorsign_abs_bf16x2",
     "llvm.nvvm.fmax.ftz.nan.xorsign.abs.f" => "__nvvm_fmax_ftz_nan_xorsign_abs_f",
+    "llvm.nvvm.fmax.ftz.xorsign.abs.bf16" => "__nvvm_fmax_ftz_xorsign_abs_bf16",
+    "llvm.nvvm.fmax.ftz.xorsign.abs.bf16x2" => "__nvvm_fmax_ftz_xorsign_abs_bf16x2",
     "llvm.nvvm.fmax.ftz.xorsign.abs.f" => "__nvvm_fmax_ftz_xorsign_abs_f",
     "llvm.nvvm.fmax.nan.bf16" => "__nvvm_fmax_nan_bf16",
     "llvm.nvvm.fmax.nan.bf16x2" => "__nvvm_fmax_nan_bf16x2",
@@ -3113,11 +3161,17 @@ match name {
     "llvm.nvvm.fmin.bf16x2" => "__nvvm_fmin_bf16x2",
     "llvm.nvvm.fmin.d" => "__nvvm_fmin_d",
     "llvm.nvvm.fmin.f" => "__nvvm_fmin_f",
-    "llvm.nvvm.fmin.f16" => "__nvvm_fmin_f16",
-    "llvm.nvvm.fmin.f16x2" => "__nvvm_fmin_f16x2",
+    "llvm.nvvm.fmin.ftz.bf16" => "__nvvm_fmin_ftz_bf16",
+    "llvm.nvvm.fmin.ftz.bf16x2" => "__nvvm_fmin_ftz_bf16x2",
     "llvm.nvvm.fmin.ftz.f" => "__nvvm_fmin_ftz_f",
+    "llvm.nvvm.fmin.ftz.nan.bf16" => "__nvvm_fmin_ftz_nan_bf16",
+    "llvm.nvvm.fmin.ftz.nan.bf16x2" => "__nvvm_fmin_ftz_nan_bf16x2",
     "llvm.nvvm.fmin.ftz.nan.f" => "__nvvm_fmin_ftz_nan_f",
+    "llvm.nvvm.fmin.ftz.nan.xorsign.abs.bf16" => "__nvvm_fmin_ftz_nan_xorsign_abs_bf16",
+    "llvm.nvvm.fmin.ftz.nan.xorsign.abs.bf16x2" => "__nvvm_fmin_ftz_nan_xorsign_abs_bf16x2",
     "llvm.nvvm.fmin.ftz.nan.xorsign.abs.f" => "__nvvm_fmin_ftz_nan_xorsign_abs_f",
+    "llvm.nvvm.fmin.ftz.xorsign.abs.bf16" => "__nvvm_fmin_ftz_xorsign_abs_bf16",
+    "llvm.nvvm.fmin.ftz.xorsign.abs.bf16x2" => "__nvvm_fmin_ftz_xorsign_abs_bf16x2",
     "llvm.nvvm.fmin.ftz.xorsign.abs.f" => "__nvvm_fmin_ftz_xorsign_abs_f",
     "llvm.nvvm.fmin.nan.bf16" => "__nvvm_fmin_nan_bf16",
     "llvm.nvvm.fmin.nan.bf16x2" => "__nvvm_fmin_nan_bf16x2",
@@ -4213,6 +4267,28 @@ match name {
     "llvm.r600.read.tgid.x" => "__builtin_r600_read_tgid_x",
     "llvm.r600.read.tgid.y" => "__builtin_r600_read_tgid_y",
     "llvm.r600.read.tgid.z" => "__builtin_r600_read_tgid_z",
+    // riscv
+    "llvm.riscv.aes32dsi" => "__builtin_riscv_aes32dsi",
+    "llvm.riscv.aes32dsmi" => "__builtin_riscv_aes32dsmi",
+    "llvm.riscv.aes32esi" => "__builtin_riscv_aes32esi",
+    "llvm.riscv.aes32esmi" => "__builtin_riscv_aes32esmi",
+    "llvm.riscv.aes64ds" => "__builtin_riscv_aes64ds",
+    "llvm.riscv.aes64dsm" => "__builtin_riscv_aes64dsm",
+    "llvm.riscv.aes64es" => "__builtin_riscv_aes64es",
+    "llvm.riscv.aes64esm" => "__builtin_riscv_aes64esm",
+    "llvm.riscv.aes64im" => "__builtin_riscv_aes64im",
+    "llvm.riscv.aes64ks1i" => "__builtin_riscv_aes64ks1i",
+    "llvm.riscv.aes64ks2" => "__builtin_riscv_aes64ks2",
+    "llvm.riscv.sha512sig0" => "__builtin_riscv_sha512sig0",
+    "llvm.riscv.sha512sig0h" => "__builtin_riscv_sha512sig0h",
+    "llvm.riscv.sha512sig0l" => "__builtin_riscv_sha512sig0l",
+    "llvm.riscv.sha512sig1" => "__builtin_riscv_sha512sig1",
+    "llvm.riscv.sha512sig1h" => "__builtin_riscv_sha512sig1h",
+    "llvm.riscv.sha512sig1l" => "__builtin_riscv_sha512sig1l",
+    "llvm.riscv.sha512sum0" => "__builtin_riscv_sha512sum0",
+    "llvm.riscv.sha512sum0r" => "__builtin_riscv_sha512sum0r",
+    "llvm.riscv.sha512sum1" => "__builtin_riscv_sha512sum1",
+    "llvm.riscv.sha512sum1r" => "__builtin_riscv_sha512sum1r",
     // s390
     "llvm.s390.efpc" => "__builtin_s390_efpc",
     "llvm.s390.etnd" => "__builtin_tx_nesting_depth",
@@ -5912,6 +5988,18 @@ match name {
     "llvm.x86.avx2.vpdpbuud.256" => "__builtin_ia32_vpdpbuud256",
     "llvm.x86.avx2.vpdpbuuds.128" => "__builtin_ia32_vpdpbuuds128",
     "llvm.x86.avx2.vpdpbuuds.256" => "__builtin_ia32_vpdpbuuds256",
+    "llvm.x86.avx2.vpdpwsud.128" => "__builtin_ia32_vpdpwsud128",
+    "llvm.x86.avx2.vpdpwsud.256" => "__builtin_ia32_vpdpwsud256",
+    "llvm.x86.avx2.vpdpwsuds.128" => "__builtin_ia32_vpdpwsuds128",
+    "llvm.x86.avx2.vpdpwsuds.256" => "__builtin_ia32_vpdpwsuds256",
+    "llvm.x86.avx2.vpdpwusd.128" => "__builtin_ia32_vpdpwusd128",
+    "llvm.x86.avx2.vpdpwusd.256" => "__builtin_ia32_vpdpwusd256",
+    "llvm.x86.avx2.vpdpwusds.128" => "__builtin_ia32_vpdpwusds128",
+    "llvm.x86.avx2.vpdpwusds.256" => "__builtin_ia32_vpdpwusds256",
+    "llvm.x86.avx2.vpdpwuud.128" => "__builtin_ia32_vpdpwuud128",
+    "llvm.x86.avx2.vpdpwuud.256" => "__builtin_ia32_vpdpwuud256",
+    "llvm.x86.avx2.vpdpwuuds.128" => "__builtin_ia32_vpdpwuuds128",
+    "llvm.x86.avx2.vpdpwuuds.256" => "__builtin_ia32_vpdpwuuds256",
     "llvm.x86.avx2.vperm2i128" => "__builtin_ia32_permti256",
     "llvm.x86.avx512.add.pd.512" => "__builtin_ia32_addpd512",
     "llvm.x86.avx512.add.ps.512" => "__builtin_ia32_addps512",
@@ -7909,6 +7997,16 @@ match name {
     "llvm.x86.vgf2p8mulb.128" => "__builtin_ia32_vgf2p8mulb_v16qi",
     "llvm.x86.vgf2p8mulb.256" => "__builtin_ia32_vgf2p8mulb_v32qi",
     "llvm.x86.vgf2p8mulb.512" => "__builtin_ia32_vgf2p8mulb_v64qi",
+    "llvm.x86.vsha512msg1" => "__builtin_ia32_vsha512msg1",
+    "llvm.x86.vsha512msg2" => "__builtin_ia32_vsha512msg2",
+    "llvm.x86.vsha512rnds2" => "__builtin_ia32_vsha512rnds2",
+    "llvm.x86.vsm3msg1" => "__builtin_ia32_vsm3msg1",
+    "llvm.x86.vsm3msg2" => "__builtin_ia32_vsm3msg2",
+    "llvm.x86.vsm3rnds2" => "__builtin_ia32_vsm3rnds2",
+    "llvm.x86.vsm4key4128" => "__builtin_ia32_vsm4key4128",
+    "llvm.x86.vsm4key4256" => "__builtin_ia32_vsm4key4256",
+    "llvm.x86.vsm4rnds4128" => "__builtin_ia32_vsm4rnds4128",
+    "llvm.x86.vsm4rnds4256" => "__builtin_ia32_vsm4rnds4256",
     "llvm.x86.wbinvd" => "__builtin_ia32_wbinvd",
     "llvm.x86.wbnoinvd" => "__builtin_ia32_wbnoinvd",
     "llvm.x86.wrfsbase.32" => "__builtin_ia32_wrfsbase32",
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
index f28348380d7..5996623bdc5 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
@@ -236,11 +236,17 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(builder: &Builder<'a, 'gcc
                 let arg2 = builder.context.new_cast(None, arg2, arg2_type);
                 args = vec![new_args[0], arg2].into();
             },
+            // These builtins are sent one more argument than needed.
             "__builtin_prefetch" => {
                 let mut new_args = args.to_vec();
                 new_args.pop();
                 args = new_args.into();
             },
+            // The GCC version returns one value of the tuple through a pointer.
+            "__builtin_ia32_rdrand64_step" => {
+                let arg = builder.current_func().new_local(None, builder.ulonglong_type, "return_rdrand_arg");
+                args = vec![arg.get_address(None)].into();
+            },
             _ => (),
         }
     }
@@ -361,6 +367,19 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>(builder: &Builder<'a, 'gcc,
             // builtin twice, we overwrite the return value with a dummy value.
             return_value = builder.context.new_rvalue_zero(builder.int_type);
         },
+        "__builtin_ia32_rdrand64_step" => {
+            let random_number = args[0].dereference(None).to_rvalue();
+            let success_variable = builder.current_func().new_local(None, return_value.get_type(), "success");
+            builder.llbb().add_assignment(None, success_variable, return_value);
+
+            let field1 = builder.context.new_field(None, random_number.get_type(), "random_number");
+            let field2 = builder.context.new_field(None, return_value.get_type(), "success");
+            let struct_type = builder.context.new_struct_type(None, "rdrand_result", &[field1, field2]);
+            return_value = builder.context.new_struct_constructor(None, struct_type.as_type(), None, &[
+                random_number,
+                success_variable.to_rvalue(),
+            ]);
+        },
         _ => (),
     }
 
@@ -613,6 +632,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
         "llvm.fshr.v8i16" => "__builtin_ia32_vpshrdv_v8hi",
         "llvm.x86.fma.vfmadd.sd" => "__builtin_ia32_vfmaddsd3",
         "llvm.x86.fma.vfmadd.ss" => "__builtin_ia32_vfmaddss3",
+        "llvm.x86.rdrand.64" => "__builtin_ia32_rdrand64_step",
 
         // The above doc points to unknown builtins for the following, so override them:
         "llvm.x86.avx2.gather.d.d" => "__builtin_ia32_gathersiv4si",
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
index 68a087a1d7f..9caed459a29 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -10,9 +10,9 @@ use rustc_codegen_ssa::base::wants_msvc_seh;
 use rustc_codegen_ssa::common::IntPredicate;
 use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
 use rustc_codegen_ssa::mir::place::PlaceRef;
-use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
+use rustc_codegen_ssa::traits::{ArgAbiMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
 #[cfg(feature="master")]
-use rustc_codegen_ssa::traits::MiscMethods;
+use rustc_codegen_ssa::traits::{BaseTypeMethods, MiscMethods};
 use rustc_codegen_ssa::errors::InvalidMonomorphization;
 use rustc_middle::bug;
 use rustc_middle::ty::{self, Instance, Ty};
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index ce7e31682f1..9c18fc4a0dc 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -2,6 +2,12 @@
  * TODO(antoyo): implement equality in libgccjit based on https://zpz.github.io/blog/overloading-equality-operator-in-cpp-class-hierarchy/ (for type equality?)
  * TODO(antoyo): support #[inline] attributes.
  * TODO(antoyo): support LTO (gcc's equivalent to Full LTO is -flto -flto-partition=one — https://documentation.suse.com/sbp/all/html/SBP-GCC-10/index.html).
+ * For Thin LTO, this might be helpful:
+ * In gcc 4.6 -fwhopr was removed and became default with -flto. The non-whopr path can still be executed via -flto-partition=none.
+ *
+ * Maybe some missing optizations enabled by rustc's LTO is in there: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
+ * Like -fipa-icf (should be already enabled) and maybe -fdevirtualize-at-ltrans.
+ * TODO: disable debug info always being emitted. Perhaps this slows down things?
  *
  * TODO(antoyo): remove the patches.
  */
@@ -28,6 +34,7 @@ extern crate rustc_codegen_ssa;
 extern crate rustc_data_structures;
 extern crate rustc_errors;
 extern crate rustc_fluent_macro;
+extern crate rustc_fs_util;
 extern crate rustc_hir;
 extern crate rustc_macros;
 extern crate rustc_metadata;
@@ -35,7 +42,8 @@ extern crate rustc_middle;
 extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
-extern crate tempfile;
+#[macro_use]
+extern crate tracing;
 
 // This prevents duplicating functions and statics that are already part of the host rustc process.
 #[allow(unused_extern_crates)]
@@ -57,6 +65,7 @@ mod coverageinfo;
 mod debuginfo;
 mod declare;
 mod errors;
+mod gcc_util;
 mod int;
 mod intrinsic;
 mod mono_item;
@@ -64,18 +73,29 @@ mod type_;
 mod type_of;
 
 use std::any::Any;
-use std::sync::{Arc, Mutex};
-
-use crate::errors::LTONotSupported;
-use gccjit::{Context, OptimizationLevel, CType};
+use std::fmt::Debug;
+use std::sync::Arc;
+use std::sync::Mutex;
+#[cfg(not(feature="master"))]
+use std::sync::atomic::AtomicBool;
+#[cfg(not(feature="master"))]
+use std::sync::atomic::Ordering;
+
+use gccjit::{Context, OptimizationLevel};
+#[cfg(feature="master")]
+use gccjit::TargetInfo;
+#[cfg(not(feature="master"))]
+use gccjit::CType;
+use errors::LTONotSupported;
 use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
 use rustc_codegen_ssa::base::codegen_crate;
 use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn};
 use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
 use rustc_codegen_ssa::target_features::supported_target_features;
-use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
 use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::sync::IntoDynSyncSend;
+use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ThinBufferMethods, WriteBackendMethods};
 use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, SubdiagnosticMessage};
 use rustc_fluent_macro::fluent_messages;
 use rustc_metadata::EncodedMetadata;
@@ -88,6 +108,9 @@ use rustc_span::Symbol;
 use rustc_span::fatal_error::FatalError;
 use tempfile::TempDir;
 
+use crate::back::lto::ModuleBuffer;
+use crate::gcc_util::target_cpu;
+
 fluent_messages! { "../messages.ftl" }
 
 pub struct PrintOnPanic<F: Fn() -> String>(pub F);
@@ -100,9 +123,47 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
     }
 }
 
+#[cfg(not(feature="master"))]
+#[derive(Debug)]
+pub struct TargetInfo {
+    supports_128bit_integers: AtomicBool,
+}
+
+#[cfg(not(feature="master"))]
+impl TargetInfo {
+    fn cpu_supports(&self, _feature: &str) -> bool {
+        false
+    }
+
+    fn supports_128bit_int(&self) -> bool {
+        self.supports_128bit_integers.load(Ordering::SeqCst)
+    }
+}
+
+#[derive(Clone)]
+pub struct LockedTargetInfo {
+    info: Arc<Mutex<IntoDynSyncSend<TargetInfo>>>,
+}
+
+impl Debug for LockedTargetInfo {
+    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.info.lock().expect("lock").fmt(formatter)
+    }
+}
+
+impl LockedTargetInfo {
+    fn cpu_supports(&self, feature: &str) -> bool {
+        self.info.lock().expect("lock").cpu_supports(feature)
+    }
+
+    fn supports_128bit_int(&self) -> bool {
+        self.info.lock().expect("lock").supports_128bit_int()
+    }
+}
+
 #[derive(Clone)]
 pub struct GccCodegenBackend {
-    supports_128bit_integers: Arc<Mutex<bool>>,
+    target_info: LockedTargetInfo,
 }
 
 impl CodegenBackend for GccCodegenBackend {
@@ -112,24 +173,40 @@ impl CodegenBackend for GccCodegenBackend {
 
     fn init(&self, sess: &Session) {
         #[cfg(feature="master")]
+        {
+            let target_cpu = target_cpu(sess);
+
+            // Get the second TargetInfo with the correct CPU features by setting the arch.
+            let context = Context::default();
+            if target_cpu != "generic" {
+                context.add_command_line_option(&format!("-march={}", target_cpu));
+            }
+
+            **self.target_info.info.lock().expect("lock") = context.get_target_info();
+        }
+
+        #[cfg(feature="master")]
         gccjit::set_global_personality_function_name(b"rust_eh_personality\0");
-        if sess.lto() != Lto::No {
+        if sess.lto() == Lto::Thin {
             sess.emit_warning(LTONotSupported {});
         }
 
-        let temp_dir = TempDir::new().expect("cannot create temporary directory");
-        let temp_file = temp_dir.into_path().join("result.asm");
-        let check_context = Context::default();
-        check_context.set_print_errors_to_stderr(false);
-        let _int128_ty = check_context.new_c_type(CType::UInt128t);
-        // NOTE: we cannot just call compile() as this would require other files than libgccjit.so.
-        check_context.compile_to_file(gccjit::OutputKind::Assembler, temp_file.to_str().expect("path to str"));
-        *self.supports_128bit_integers.lock().expect("lock") = check_context.get_last_error() == Ok(None);
+        #[cfg(not(feature="master"))]
+        {
+            let temp_dir = TempDir::new().expect("cannot create temporary directory");
+            let temp_file = temp_dir.into_path().join("result.asm");
+            let check_context = Context::default();
+            check_context.set_print_errors_to_stderr(false);
+            let _int128_ty = check_context.new_c_type(CType::UInt128t);
+            // NOTE: we cannot just call compile() as this would require other files than libgccjit.so.
+            check_context.compile_to_file(gccjit::OutputKind::Assembler, temp_file.to_str().expect("path to str"));
+            self.target_info.info.lock().expect("lock").supports_128bit_integers.store(check_context.get_last_error() == Ok(None), Ordering::SeqCst);
+        }
     }
 
     fn provide(&self, providers: &mut Providers) {
-        // FIXME(antoyo) compute list of enabled features from cli flags
-        providers.global_backend_features = |_tcx, ()| vec![];
+        providers.global_backend_features =
+            |tcx, ()| gcc_util::global_gcc_features(tcx.sess, true)
     }
 
     fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
@@ -160,7 +237,7 @@ impl CodegenBackend for GccCodegenBackend {
     }
 
     fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
-        target_features(sess, allow_unstable)
+        target_features(sess, allow_unstable, &self.target_info)
     }
 }
 
@@ -168,13 +245,18 @@ impl ExtraBackendMethods for GccCodegenBackend {
     fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind) -> Self::Module {
         let mut mods = GccContext {
             context: Context::default(),
+            should_combine_object_files: false,
+            temp_dir: None,
         };
+
+        // TODO(antoyo): only set for x86.
+        mods.context.add_command_line_option("-masm=intel");
         unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, alloc_error_handler_kind); }
         mods
     }
 
     fn compile_codegen_unit(&self, tcx: TyCtxt<'_>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
-        base::compile_codegen_unit(tcx, cgu_name, *self.supports_128bit_integers.lock().expect("lock"))
+        base::compile_codegen_unit(tcx, cgu_name, self.target_info.clone())
     }
 
     fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel, _features: &[String]) -> TargetMachineFactoryFn<Self> {
@@ -185,14 +267,6 @@ impl ExtraBackendMethods for GccCodegenBackend {
     }
 }
 
-pub struct ModuleBuffer;
-
-impl ModuleBufferMethods for ModuleBuffer {
-    fn data(&self) -> &[u8] {
-        unimplemented!();
-    }
-}
-
 pub struct ThinBuffer;
 
 impl ThinBufferMethods for ThinBuffer {
@@ -203,6 +277,9 @@ impl ThinBufferMethods for ThinBuffer {
 
 pub struct GccContext {
     context: Context<'static>,
+    should_combine_object_files: bool,
+    // Temporary directory used by LTO. We keep it here so that it's not removed before linking.
+    temp_dir: Option<TempDir>,
 }
 
 unsafe impl Send for GccContext {}
@@ -217,18 +294,8 @@ impl WriteBackendMethods for GccCodegenBackend {
     type ThinData = ();
     type ThinBuffer = ThinBuffer;
 
-    fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLtoInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
-        // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
-        // NOTE: implemented elsewhere.
-        // TODO(antoyo): what is implemented elsewhere ^ ?
-        let module =
-            match modules.remove(0) {
-                FatLtoInput::InMemory(module) => module,
-                FatLtoInput::Serialized { .. } => {
-                    unimplemented!();
-                }
-            };
-        Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
+    fn run_fat_lto(cgcx: &CodegenContext<Self>, modules: Vec<FatLtoInput<Self>>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
+        back::lto::run_fat(cgcx, modules, cached_modules)
     }
 
     fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
@@ -277,8 +344,19 @@ impl WriteBackendMethods for GccCodegenBackend {
 /// This is the entrypoint for a hot plugged rustc_codegen_gccjit
 #[no_mangle]
 pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
+    #[cfg(feature="master")]
+    let info = {
+        // Check whether the target supports 128-bit integers.
+        let context = Context::default();
+        Arc::new(Mutex::new(IntoDynSyncSend(context.get_target_info())))
+    };
+    #[cfg(not(feature="master"))]
+    let info = Arc::new(Mutex::new(IntoDynSyncSend(TargetInfo {
+        supports_128bit_integers: AtomicBool::new(false),
+    })));
+
     Box::new(GccCodegenBackend {
-        supports_128bit_integers: Arc::new(Mutex::new(false)),
+        target_info: LockedTargetInfo { info },
     })
 }
 
@@ -297,22 +375,7 @@ fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
     }
 }
 
-fn handle_native(name: &str) -> &str {
-    if name != "native" {
-        return name;
-    }
-
-    unimplemented!();
-}
-
-pub fn target_cpu(sess: &Session) -> &str {
-    match sess.opts.cg.target_cpu {
-        Some(ref name) => handle_native(name),
-        None => handle_native(sess.target.cpu.as_ref()),
-    }
-}
-
-pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
+pub fn target_features(sess: &Session, allow_unstable: bool, target_info: &LockedTargetInfo) -> Vec<Symbol> {
     supported_target_features(sess)
         .iter()
         .filter_map(
@@ -321,26 +384,13 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
             },
         )
         .filter(|_feature| {
-            // TODO(antoyo): implement a way to get enabled feature in libgccjit.
-            // Probably using the equivalent of __builtin_cpu_supports.
-            // TODO(antoyo): maybe use whatever outputs the following command:
-            // gcc -march=native -Q --help=target
-            #[cfg(feature="master")]
-            {
-                // NOTE: the CPU in the CI doesn't support sse4a, so disable it to make the stdarch tests pass in the CI.
-                (_feature.contains("sse") || _feature.contains("avx")) && !_feature.contains("avx512") && !_feature.contains("sse4a")
-            }
-            #[cfg(not(feature="master"))]
-            {
-                false
-            }
+            target_info.cpu_supports(_feature)
             /*
                adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512ifma,
                avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
                bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
                sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
              */
-            //false
         })
         .map(|feature| Symbol::intern(feature))
         .collect()
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index cc467801beb..c2eab295acd 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -182,6 +182,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
     /// of that field's type - this is useful for taking the address of
     /// that field and ensuring the struct has the right alignment.
     fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+        use crate::rustc_middle::ty::layout::FnAbiOf;
         // This must produce the same result for `repr(transparent)` wrappers as for the inner type!
         // In other words, this should generally not look at the type at all, but only at the
         // layout.
@@ -191,7 +192,14 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
             if let Some(&ty) = cx.scalar_types.borrow().get(&self.ty) {
                 return ty;
             }
-            let ty = self.scalar_gcc_type_at(cx, scalar, Size::ZERO);
+            let ty =
+                match *self.ty.kind() {
+                    // NOTE: we cannot remove this match like in the LLVM codegen because the call
+                    // to fn_ptr_backend_type handle the on-stack attribute.
+                    // TODO(antoyo): find a less hackish way to hande the on-stack attribute.
+                    ty::FnPtr(sig) => cx.fn_ptr_backend_type(&cx.fn_abi_of_fn_ptr(sig, ty::List::empty())),
+                    _ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),
+                };
             cx.scalar_types.borrow_mut().insert(self.ty, ty);
             return ty;
         }
diff --git a/compiler/rustc_codegen_gcc/test.sh b/compiler/rustc_codegen_gcc/test.sh
index b462e5d156b..e4cbd6fbcaf 100755
--- a/compiler/rustc_codegen_gcc/test.sh
+++ b/compiler/rustc_codegen_gcc/test.sh
@@ -3,6 +3,7 @@
 # TODO(antoyo): rewrite to cargo-make (or just) or something like that to only rebuild the sysroot when needed?
 
 set -e
+#set -x
 
 if [ -f ./gcc_path ]; then
     export GCC_PATH=$(cat gcc_path)
@@ -219,6 +220,7 @@ change-id = 115898
 [rust]
 codegen-backends = []
 deny-warnings = false
+verbose-tests = true
 
 [build]
 cargo = "$(rustup which cargo)"
@@ -345,15 +347,19 @@ function test_rustc() {
 
     git checkout -- tests/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
 
-    rm -r tests/ui/{abi*,extern/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,borrowck/,chalkify/bugs/,test*,*lto*.rs,consts/const-float-bits-reject-conv.rs,consts/issue-miri-1910.rs} || true
-    rm tests/ui/mir/mir_heavy_promoted.rs # this tests is oom-killed in the CI.
-    for test in $(rg --files-with-matches "thread|lto" tests/ui); do
+    rm -r tests/ui/{abi*,extern/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,borrowck/,chalkify/bugs/,test*,consts/const-float-bits-reject-conv.rs,consts/issue-miri-1910.rs} || true
+    rm tests/ui/mir/mir_heavy_promoted.rs # this test is oom-killed in the CI.
+    # Tests generating errors.
+    rm tests/ui/consts/const-eval/nonnull_as_ref_ub.rs tests/ui/consts/issue-94675.rs
+    for test in $(rg --files-with-matches "thread" tests/ui); do
       rm $test
     done
-    git checkout tests/ui/lto/auxiliary/dylib.rs
     git checkout tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs
     git checkout tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs
     git checkout tests/ui/macros/rfc-2011-nicer-assert-messages/auxiliary/common.rs
+    git checkout tests/ui/imports/ambiguous-1.rs
+    git checkout tests/ui/imports/ambiguous-4-extern.rs
+    git checkout tests/ui/entry-point/auxiliary/bad_main_functions.rs
 
     RUSTC_ARGS="$TEST_FLAGS -Csymbol-mangling-version=v0 -Zcodegen-backend="$(pwd)"/../target/"$CHANNEL"/librustc_codegen_gcc."$dylib_ext" --sysroot "$(pwd)"/../build_sysroot/sysroot"
 
diff --git a/compiler/rustc_codegen_gcc/tests/run/abort1.rs b/compiler/rustc_codegen_gcc/tests/run/abort1.rs
index 25041d93e74..44297e12779 100644
--- a/compiler/rustc_codegen_gcc/tests/run/abort1.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/abort1.rs
@@ -3,7 +3,8 @@
 // Run-time:
 //   status: signal
 
-#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/abort2.rs b/compiler/rustc_codegen_gcc/tests/run/abort2.rs
index e7443c8dbe5..ce816927123 100644
--- a/compiler/rustc_codegen_gcc/tests/run/abort2.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/abort2.rs
@@ -3,7 +3,8 @@
 // Run-time:
 //   status: signal
 
-#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/array.rs b/compiler/rustc_codegen_gcc/tests/run/array.rs
index 49b28d98f2f..afd0eed8200 100644
--- a/compiler/rustc_codegen_gcc/tests/run/array.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/array.rs
@@ -7,7 +7,8 @@
 //     5
 //     10
 
-#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs
index 38c1eac7adf..507b65ca049 100644
--- a/compiler/rustc_codegen_gcc/tests/run/asm.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/asm.rs
@@ -124,7 +124,7 @@ fn main() {
     // check const (ATT syntax)
     let mut x: u64 = 42;
     unsafe {
-        asm!("add {}, {}",
+        asm!("add ${}, {}",
             const 1,
             inout(reg) x,
             options(att_syntax)
diff --git a/compiler/rustc_codegen_gcc/tests/run/assign.rs b/compiler/rustc_codegen_gcc/tests/run/assign.rs
index 427c1a25033..5b0db2da294 100644
--- a/compiler/rustc_codegen_gcc/tests/run/assign.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/assign.rs
@@ -5,8 +5,8 @@
 //     7 8
 //     10
 
-#![allow(unused_attributes)]
-#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)]
+#![allow(internal_features, unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs, track_caller)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/closure.rs b/compiler/rustc_codegen_gcc/tests/run/closure.rs
index 8daa681abf7..4ce528f8680 100644
--- a/compiler/rustc_codegen_gcc/tests/run/closure.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/closure.rs
@@ -9,7 +9,8 @@
 //     Both args: 11
 
 #![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics,
-    unboxed_closures)]
+    unboxed_closures, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/condition.rs b/compiler/rustc_codegen_gcc/tests/run/condition.rs
index b7a13081dea..1b3ae6dc004 100644
--- a/compiler/rustc_codegen_gcc/tests/run/condition.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/condition.rs
@@ -5,7 +5,8 @@
 //   stdout: true
 //     1
 
-#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs
index c02cfd2a85f..2d78ef12aa7 100644
--- a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs
@@ -4,6 +4,7 @@
 //   status: 0
 
 #![feature(auto_traits, lang_items, no_core, start)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/exit.rs b/compiler/rustc_codegen_gcc/tests/run/exit.rs
index 956e53dd4aa..bf1cbeef302 100644
--- a/compiler/rustc_codegen_gcc/tests/run/exit.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/exit.rs
@@ -4,6 +4,7 @@
 //   status: 2
 
 #![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs
index eeab3520951..be7a233efda 100644
--- a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs
@@ -4,6 +4,7 @@
 //   status: 1
 
 #![feature(auto_traits, lang_items, no_core, start)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs
index 8a196f774c8..96030359772 100644
--- a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs
@@ -4,7 +4,8 @@
 //   status: 0
 //   stdout: 1
 
-#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/gep.rs b/compiler/rustc_codegen_gcc/tests/run/gep.rs
new file mode 100644
index 00000000000..c3d1672cff5
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/tests/run/gep.rs
@@ -0,0 +1,10 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+
+fn main() {
+    let mut value = (1, 1);
+    let ptr = &mut value as *mut (i32, i32);
+    println!("{:?}", ptr.wrapping_offset(10));
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/int_overflow.rs b/compiler/rustc_codegen_gcc/tests/run/int_overflow.rs
index c3fcb3c0a2a..08fa087fccd 100644
--- a/compiler/rustc_codegen_gcc/tests/run/int_overflow.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/int_overflow.rs
@@ -4,8 +4,8 @@
 //   stdout: Success
 //   status: signal
 
-#![allow(unused_attributes)]
-#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![allow(internal_features, unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs
index 2a2ea8b8bf0..194e55a3dea 100644
--- a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs
@@ -7,8 +7,8 @@
 //     6
 //     11
 
-#![allow(unused_attributes)]
-#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)]
+#![allow(internal_features, unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs, track_caller)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/operations.rs b/compiler/rustc_codegen_gcc/tests/run/operations.rs
index 67b9f241dbb..2d781670873 100644
--- a/compiler/rustc_codegen_gcc/tests/run/operations.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/operations.rs
@@ -5,8 +5,8 @@
 //     39
 //     10
 
-#![allow(unused_attributes)]
-#![feature(auto_traits, lang_items, no_core, start, intrinsics, arbitrary_self_types)]
+#![allow(internal_features, unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, arbitrary_self_types, rustc_attrs)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs
index da8a8295d56..09d77abe27c 100644
--- a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs
@@ -4,7 +4,8 @@
 //   status: 0
 //   stdout: 1
 
-#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs
index 6fa10dca06f..8d40deb8c85 100644
--- a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs
@@ -7,6 +7,7 @@
 //     42
 
 #![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/slice.rs b/compiler/rustc_codegen_gcc/tests/run/slice.rs
index 96f1c4792e5..1262c86c810 100644
--- a/compiler/rustc_codegen_gcc/tests/run/slice.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/slice.rs
@@ -4,7 +4,8 @@
 //   status: 0
 //   stdout: 5
 
-#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/static.rs b/compiler/rustc_codegen_gcc/tests/run/static.rs
index 19201f1df26..0b933754c29 100644
--- a/compiler/rustc_codegen_gcc/tests/run/static.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/static.rs
@@ -9,7 +9,8 @@
 //      12
 //      1
 
-#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/structs.rs b/compiler/rustc_codegen_gcc/tests/run/structs.rs
index 6c8884855ac..d6455667400 100644
--- a/compiler/rustc_codegen_gcc/tests/run/structs.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/structs.rs
@@ -6,6 +6,7 @@
 //     2
 
 #![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tests/run/tuple.rs b/compiler/rustc_codegen_gcc/tests/run/tuple.rs
index 0b670bf2674..8a7d85ae867 100644
--- a/compiler/rustc_codegen_gcc/tests/run/tuple.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/tuple.rs
@@ -5,6 +5,7 @@
 //   stdout: 3
 
 #![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+#![allow(internal_features)]
 
 #![no_std]
 #![no_core]
diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
index 83abe145e64..90fb7bfad27 100644
--- a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
+++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
@@ -46,10 +46,10 @@ def convert_to_string(content):
 
 
 def extract_instrinsics_from_llvm(llvm_path, intrinsics):
-    p = subprocess.Popen(
-        ["llvm-tblgen", "llvm/IR/Intrinsics.td"],
-        cwd=os.path.join(llvm_path, "llvm/include"),
-        stdout=subprocess.PIPE)
+    command = ["llvm-tblgen", "llvm/IR/Intrinsics.td"]
+    cwd = os.path.join(llvm_path, "llvm/include")
+    print("=> Running command `{}` from `{}`".format(command, cwd))
+    p = subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE)
     output, err = p.communicate()
     lines = convert_to_string(output).splitlines()
     pos = 0
diff --git a/compiler/rustc_codegen_gcc/y.sh b/compiler/rustc_codegen_gcc/y.sh
new file mode 100755
index 00000000000..188109743e3
--- /dev/null
+++ b/compiler/rustc_codegen_gcc/y.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -e
+echo "[BUILD] build system" 1>&2
+cd build_system
+cargo build --release
+cd ..
+./build_system/target/release/y $@
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index cb5acf79135..8655aeec13d 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -19,7 +19,6 @@ use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::WorkProduct;
 use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
-use rustc_session::cgu_reuse_tracker::CguReuse;
 use rustc_session::config::{self, CrateType, Lto};
 
 use std::ffi::{CStr, CString};
@@ -584,7 +583,6 @@ fn thin_lto(
                     copy_jobs.push(work_product);
                     info!(" - {}: re-used", module_name);
                     assert!(cgcx.incr_comp_session_dir.is_some());
-                    cgcx.cgu_reuse_tracker.set_actual_reuse(module_name, CguReuse::PostLto);
                     continue;
                 }
             }
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index dcc62d314ff..fd4c9572af2 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -397,7 +397,12 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
             // extracts all the individual values.
 
             let ety = element.llvm_type(cx);
-            return Some(cx.type_vector(ety, *count));
+            if *count == 1 {
+                // Emitting `<1 x T>` would be silly; just use the scalar.
+                return Some(ety);
+            } else {
+                return Some(cx.type_vector(ety, *count));
+            }
         }
 
         // FIXME: The above only handled integer arrays; surely more things
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 34d0e2d1df6..454e2f80676 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -16,6 +16,7 @@ pathdiff = "0.2.0"
 serde_json = "1.0.59"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 regex = "1.4"
+thin-vec = "0.2.12"
 
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_arena = { path = "../rustc_arena" }
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 66482667336..5881c6236ec 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -11,6 +11,9 @@ codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing
 
 codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty
 
+codegen_ssa_cgu_not_recorded =
+    CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded
+
 codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option.
 
 codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error}
@@ -39,6 +42,8 @@ codegen_ssa_failed_to_get_layout = failed to get layout for {$ty}: {$err}
 
 codegen_ssa_failed_to_write = failed to write {$path}: {$error}
 
+codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
+
 codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
 
 codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced
@@ -46,6 +51,12 @@ codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files w
 codegen_ssa_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal`
     .note = an unsuffixed integer value, e.g., `1`, is expected
 
+codegen_ssa_incorrect_cgu_reuse_type =
+    CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
+    [one] {"at least "}
+    *[other] {""}
+    }`{$expected_reuse}`
+
 codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient.
 
 codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
@@ -153,12 +164,18 @@ codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for
 
 codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status}
 
+codegen_ssa_malformed_cgu_name =
+    found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case).
+
 codegen_ssa_metadata_object_file_write = error writing metadata object file: {$error}
 
 codegen_ssa_missing_cpp_build_tool_component = or a necessary component may be missing from the "C++ build tools" workload
 
 codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering
 
+codegen_ssa_missing_query_depgraph =
+    found CGU-reuse attribute but `-Zquery-dep-graph` was not specified
+
 codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but `link.exe` was not found
 
 codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions
@@ -166,6 +183,11 @@ codegen_ssa_multiple_external_func_decl = multiple declarations of external func
 codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple times
     .help = did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead
 
+codegen_ssa_no_field = no field `{$name}`
+
+codegen_ssa_no_module_named =
+    no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names}
+
 codegen_ssa_no_natvis_directory = error enumerating natvis directory: {$error}
 
 codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status}
@@ -297,6 +319,8 @@ codegen_ssa_unknown_atomic_operation = unknown atomic operation
 
 codegen_ssa_unknown_atomic_ordering = unknown ordering in atomic intrinsic
 
+codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
+
 codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}`
 
 codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs
index 8e22ab4083e..16bb7b12bd3 100644
--- a/compiler/rustc_incremental/src/assert_module_sources.rs
+++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs
@@ -25,16 +25,21 @@
 
 use crate::errors;
 use rustc_ast as ast;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unord::UnordSet;
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::mir::mono::CodegenUnitNameBuilder;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::cgu_reuse_tracker::*;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_session::Session;
+use rustc_span::symbol::sym;
+use rustc_span::{Span, Symbol};
+use std::borrow::Cow;
+use std::fmt;
 use thin_vec::ThinVec;
 
 #[allow(missing_docs)]
-pub fn assert_module_sources(tcx: TyCtxt<'_>) {
+pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTracker)) {
     tcx.dep_graph.with_ignore(|| {
         if tcx.sess.opts.incremental.is_none() {
             return;
@@ -43,21 +48,34 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) {
         let available_cgus =
             tcx.collect_and_partition_mono_items(()).1.iter().map(|cgu| cgu.name()).collect();
 
-        let ams = AssertModuleSource { tcx, available_cgus };
+        let mut ams = AssertModuleSource {
+            tcx,
+            available_cgus,
+            cgu_reuse_tracker: if tcx.sess.opts.unstable_opts.query_dep_graph {
+                CguReuseTracker::new()
+            } else {
+                CguReuseTracker::new_disabled()
+            },
+        };
 
         for attr in tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) {
             ams.check_attr(attr);
         }
-    })
+
+        set_reuse(&mut ams.cgu_reuse_tracker);
+
+        ams.cgu_reuse_tracker.check_expected_reuse(tcx.sess);
+    });
 }
 
 struct AssertModuleSource<'tcx> {
     tcx: TyCtxt<'tcx>,
     available_cgus: UnordSet<Symbol>,
+    cgu_reuse_tracker: CguReuseTracker,
 }
 
 impl<'tcx> AssertModuleSource<'tcx> {
-    fn check_attr(&self, attr: &ast::Attribute) {
+    fn check_attr(&mut self, attr: &ast::Attribute) {
         let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) {
             (CguReuse::PreLto, ComparisonKind::AtLeast)
         } else if attr.has_name(sym::rustc_partition_codegened) {
@@ -129,7 +147,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
             });
         }
 
-        self.tcx.sess.cgu_reuse_tracker.set_expectation(
+        self.cgu_reuse_tracker.set_expectation(
             cgu_name,
             &user_path,
             attr.span,
@@ -169,3 +187,109 @@ impl<'tcx> AssertModuleSource<'tcx> {
         false
     }
 }
+
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
+pub enum CguReuse {
+    No,
+    PreLto,
+    PostLto,
+}
+
+impl fmt::Display for CguReuse {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            CguReuse::No => write!(f, "No"),
+            CguReuse::PreLto => write!(f, "PreLto "),
+            CguReuse::PostLto => write!(f, "PostLto "),
+        }
+    }
+}
+
+impl IntoDiagnosticArg for CguReuse {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ComparisonKind {
+    Exact,
+    AtLeast,
+}
+
+struct TrackerData {
+    actual_reuse: FxHashMap<String, CguReuse>,
+    expected_reuse: FxHashMap<String, (String, Span, CguReuse, ComparisonKind)>,
+}
+
+pub struct CguReuseTracker {
+    data: Option<TrackerData>,
+}
+
+impl CguReuseTracker {
+    fn new() -> CguReuseTracker {
+        let data =
+            TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() };
+
+        CguReuseTracker { data: Some(data) }
+    }
+
+    fn new_disabled() -> CguReuseTracker {
+        CguReuseTracker { data: None }
+    }
+
+    pub fn set_actual_reuse(&mut self, cgu_name: &str, kind: CguReuse) {
+        if let Some(data) = &mut self.data {
+            debug!("set_actual_reuse({cgu_name:?}, {kind:?})");
+
+            let prev_reuse = data.actual_reuse.insert(cgu_name.to_string(), kind);
+            assert!(prev_reuse.is_none());
+        }
+    }
+
+    fn set_expectation(
+        &mut self,
+        cgu_name: Symbol,
+        cgu_user_name: &str,
+        error_span: Span,
+        expected_reuse: CguReuse,
+        comparison_kind: ComparisonKind,
+    ) {
+        if let Some(data) = &mut self.data {
+            debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})");
+
+            data.expected_reuse.insert(
+                cgu_name.to_string(),
+                (cgu_user_name.to_string(), error_span, expected_reuse, comparison_kind),
+            );
+        }
+    }
+
+    fn check_expected_reuse(&self, sess: &Session) {
+        if let Some(ref data) = self.data {
+            for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in
+                &data.expected_reuse
+            {
+                if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) {
+                    let (error, at_least) = match comparison_kind {
+                        ComparisonKind::Exact => (expected_reuse != actual_reuse, false),
+                        ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true),
+                    };
+
+                    if error {
+                        let at_least = if at_least { 1 } else { 0 };
+                        errors::IncorrectCguReuseType {
+                            span: *error_span,
+                            cgu_user_name,
+                            actual_reuse,
+                            expected_reuse,
+                            at_least,
+                        };
+                    }
+                } else {
+                    sess.emit_fatal(errors::CguNotRecorded { cgu_user_name, cgu_name });
+                }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 464424409c7..28a51711b93 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -2978,8 +2978,9 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
     }
 
     // 1. Implement the "self-contained" part of this feature by adding rustc distribution
-    //    directories to the tool's search path.
-    if sess.opts.cg.link_self_contained.linker() {
+    // directories to the tool's search path:
+    // - if the self-contained linker is enabled on the CLI.
+    if sess.opts.cg.link_self_contained.is_linker_enabled() {
         for path in sess.get_tools_search_paths(false) {
             cmd.arg({
                 let mut arg = OsString::from("-B");
@@ -2990,7 +2991,7 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
     }
 
     // 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of
-    //    `lld` as the linker.
+    // `lld` as the linker.
     cmd.arg("-fuse-ld=lld");
 
     if !flavor.is_gnu() {
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index f192747c8ab..3d6a2124334 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -26,7 +26,6 @@ use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::middle::exported_symbols::SymbolExportInfo;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::cgu_reuse_tracker::CguReuseTracker;
 use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType};
 use rustc_session::config::{Passes, SwitchWithOptPath};
 use rustc_session::Session;
@@ -366,8 +365,6 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     /// The incremental compilation session directory, or None if we are not
     /// compiling incrementally
     pub incr_comp_session_dir: Option<PathBuf>,
-    /// Used to update CGU re-use information during the thinlto phase.
-    pub cgu_reuse_tracker: CguReuseTracker,
     /// Channel back to the main control thread to send messages to
     pub coordinator_send: Sender<Box<dyn Any + Send>>,
 }
@@ -1119,7 +1116,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
         remark: sess.opts.cg.remark.clone(),
         remark_dir,
         incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
-        cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
         coordinator_send,
         expanded_args: tcx.sess.expanded_args.clone(),
         diag_emitter: shared_emitter.clone(),
@@ -1969,8 +1965,6 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
             }
         });
 
-        sess.cgu_reuse_tracker.check_expected_reuse(sess);
-
         sess.abort_if_errors();
 
         let work_products =
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 1e4ea73a172..198e5696357 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -1,3 +1,4 @@
+use crate::assert_module_sources::CguReuse;
 use crate::back::link::are_upstream_rust_objects_already_included;
 use crate::back::metadata::create_compressed_metadata_file;
 use crate::back::write::{
@@ -31,7 +32,6 @@ use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
-use rustc_session::cgu_reuse_tracker::CguReuse;
 use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
 use rustc_session::Session;
 use rustc_span::symbol::sym;
@@ -683,6 +683,13 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
         codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect::<Vec<_>>()
     });
 
+    crate::assert_module_sources::assert_module_sources(tcx, &|cgu_reuse_tracker| {
+        for (i, cgu) in codegen_units.iter().enumerate() {
+            let cgu_reuse = cgu_reuse[i];
+            cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
+        }
+    });
+
     let mut total_codegen_time = Duration::new(0, 0);
     let start_rss = tcx.sess.opts.unstable_opts.time_passes.then(|| get_resident_set_size());
 
@@ -727,7 +734,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
         ongoing_codegen.check_for_errors(tcx.sess);
 
         let cgu_reuse = cgu_reuse[i];
-        tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
 
         match cgu_reuse {
             CguReuse::No => {
@@ -994,7 +1000,7 @@ pub fn provide(providers: &mut Providers) {
     };
 }
 
-fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
+pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
     if !tcx.dep_graph.is_fully_enabled() {
         return CguReuse::No;
     }
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 39b2fa37fbf..ed6ac9f9c5d 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1,5 +1,6 @@
 //! Errors emitted by codegen_ssa
 
+use crate::assert_module_sources::CguReuse;
 use crate::back::command::Command;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
@@ -17,6 +18,74 @@ use std::path::{Path, PathBuf};
 use std::process::ExitStatus;
 
 #[derive(Diagnostic)]
+#[diag(codegen_ssa_incorrect_cgu_reuse_type)]
+pub struct IncorrectCguReuseType<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub cgu_user_name: &'a str,
+    pub actual_reuse: CguReuse,
+    pub expected_reuse: CguReuse,
+    pub at_least: u8,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_cgu_not_recorded)]
+pub struct CguNotRecorded<'a> {
+    pub cgu_user_name: &'a str,
+    pub cgu_name: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_reuse_kind)]
+pub struct UnknownReuseKind {
+    #[primary_span]
+    pub span: Span,
+    pub kind: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_missing_query_depgraph)]
+pub struct MissingQueryDepGraph {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_malformed_cgu_name)]
+pub struct MalformedCguName {
+    #[primary_span]
+    pub span: Span,
+    pub user_path: String,
+    pub crate_name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_no_module_named)]
+pub struct NoModuleNamed<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub user_path: &'a str,
+    pub cgu_name: Symbol,
+    pub cgu_names: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_field_associated_value_expected)]
+pub struct FieldAssociatedValueExpected {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_no_field)]
+pub struct NoField {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
 #[diag(codegen_ssa_lib_def_write_failure)]
 pub struct LibDefWriteFailure {
     pub error: Error,
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index f6186a290f8..156c2904f40 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -1,4 +1,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(associated_type_bounds)]
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
@@ -43,6 +46,7 @@ use std::collections::BTreeSet;
 use std::io;
 use std::path::{Path, PathBuf};
 
+pub mod assert_module_sources;
 pub mod back;
 pub mod base;
 pub mod codegen_attrs;
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 3d758cd01d3..4adfbe336af 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -1,6 +1,3 @@
-use crate::const_eval::CheckAlignment;
-use crate::errors::ConstEvalError;
-
 use either::{Left, Right};
 
 use rustc_hir::def::DefKind;
@@ -15,7 +12,9 @@ use rustc_span::source_map::Span;
 use rustc_target::abi::{self, Abi};
 
 use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
+use crate::const_eval::CheckAlignment;
 use crate::errors;
+use crate::errors::ConstEvalError;
 use crate::interpret::eval_nullary_intrinsic;
 use crate::interpret::{
     intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx,
@@ -290,14 +289,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
         key.param_env,
         // Statics (and promoteds inside statics) may access other statics, because unlike consts
         // they do not have to behave "as if" they were evaluated at runtime.
-        CompileTimeInterpreter::new(
-            CanAccessStatics::from(is_static),
-            if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
-                CheckAlignment::Error
-            } else {
-                CheckAlignment::FutureIncompat
-            },
-        ),
+        CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
     );
 
     let res = ecx.load_mir(cid.instance.def, cid.promoted);
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 865e01d0aee..166d3d45e79 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -1,10 +1,9 @@
 use rustc_hir::def::DefKind;
-use rustc_hir::{LangItem, CRATE_HIR_ID};
+use rustc_hir::LangItem;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::PointerArithmetic;
 use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::lint::builtin::INVALID_ALIGNMENT;
 use std::borrow::Borrow;
 use std::hash::Hash;
 use std::ops::ControlFlow;
@@ -21,11 +20,11 @@ use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi as CallAbi;
 
 use crate::errors::{LongRunning, LongRunningWarn};
+use crate::fluent_generated as fluent;
 use crate::interpret::{
     self, compile_time_machine, AllocId, ConstAllocation, FnArg, FnVal, Frame, ImmTy, InterpCx,
     InterpResult, OpTy, PlaceTy, Pointer, Scalar,
 };
-use crate::{errors, fluent_generated as fluent};
 
 use super::error::*;
 
@@ -65,22 +64,11 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
 
 #[derive(Copy, Clone)]
 pub enum CheckAlignment {
-    /// Ignore alignment when following relocations.
+    /// Ignore all alignment requirements.
     /// This is mainly used in interning.
     No,
     /// Hard error when dereferencing a misaligned pointer.
     Error,
-    /// Emit a future incompat lint when dereferencing a misaligned pointer.
-    FutureIncompat,
-}
-
-impl CheckAlignment {
-    pub fn should_check(&self) -> bool {
-        match self {
-            CheckAlignment::No => false,
-            CheckAlignment::Error | CheckAlignment::FutureIncompat => true,
-        }
-    }
 }
 
 #[derive(Copy, Clone, PartialEq)]
@@ -358,8 +346,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
     const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
 
     #[inline(always)]
-    fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
-        ecx.machine.check_alignment
+    fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        matches!(ecx.machine.check_alignment, CheckAlignment::Error)
     }
 
     #[inline(always)]
@@ -367,39 +355,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
         ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited()
     }
 
-    fn alignment_check_failed(
-        ecx: &InterpCx<'mir, 'tcx, Self>,
-        has: Align,
-        required: Align,
-        check: CheckAlignment,
-    ) -> InterpResult<'tcx, ()> {
-        let err = err_ub!(AlignmentCheckFailed { has, required }).into();
-        match check {
-            CheckAlignment::Error => Err(err),
-            CheckAlignment::No => span_bug!(
-                ecx.cur_span(),
-                "`alignment_check_failed` called when no alignment check requested"
-            ),
-            CheckAlignment::FutureIncompat => {
-                let (_, backtrace) = err.into_parts();
-                backtrace.print_backtrace();
-                let (span, frames) = super::get_span_and_frames(&ecx);
-
-                ecx.tcx.emit_spanned_lint(
-                    INVALID_ALIGNMENT,
-                    ecx.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
-                    span,
-                    errors::AlignmentCheckFailed {
-                        has: has.bytes(),
-                        required: required.bytes(),
-                        frames,
-                    },
-                );
-                Ok(())
-            }
-        }
-    }
-
     fn load_mir(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         instance: ty::InstanceDef<'tcx>,
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index b9f88cf6352..b9557eaf6ab 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -311,6 +311,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>,
     {
         use rustc_type_ir::sty::TyKind::*;
+
+        fn adjust_nan<
+            'mir,
+            'tcx: 'mir,
+            M: Machine<'mir, 'tcx>,
+            F1: rustc_apfloat::Float + FloatConvert<F2>,
+            F2: rustc_apfloat::Float,
+        >(
+            ecx: &InterpCx<'mir, 'tcx, M>,
+            f1: F1,
+            f2: F2,
+        ) -> F2 {
+            if f2.is_nan() { M::generate_nan(ecx, &[f1]) } else { f2 }
+        }
+
         match *dest_ty.kind() {
             // float -> uint
             Uint(t) => {
@@ -330,9 +345,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 Scalar::from_int(v, size)
             }
             // float -> f32
-            Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value),
+            Float(FloatTy::F32) => {
+                Scalar::from_f32(adjust_nan(self, f, f.convert(&mut false).value))
+            }
             // float -> f64
-            Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value),
+            Float(FloatTy::F64) => {
+                Scalar::from_f64(adjust_nan(self, f, f.convert(&mut false).value))
+            }
             // That's it.
             _ => span_bug!(self.cur_span(), "invalid float to {} cast", dest_ty),
         }
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 2c6a4de456d..1891d286a3c 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -500,6 +500,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         b: &ImmTy<'tcx, M::Provenance>,
         dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
+        assert_eq!(a.layout.ty, b.layout.ty);
+        assert!(matches!(a.layout.ty.kind(), ty::Int(..) | ty::Uint(..)));
+
         // Performs an exact division, resulting in undefined behavior where
         // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
         // First, check x % y != 0 (or if that computation overflows).
@@ -522,7 +525,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         l: &ImmTy<'tcx, M::Provenance>,
         r: &ImmTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
+        assert_eq!(l.layout.ty, r.layout.ty);
+        assert!(matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..)));
         assert!(matches!(mir_op, BinOp::Add | BinOp::Sub));
+
         let (val, overflowed) = self.overflowing_binary_op(mir_op, l, r)?;
         Ok(if overflowed {
             let size = l.layout.size;
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index aaa674a598f..9b4f9906599 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -6,16 +6,15 @@ use std::borrow::{Borrow, Cow};
 use std::fmt::Debug;
 use std::hash::Hash;
 
+use rustc_apfloat::{Float, FloatConvert};
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_middle::mir;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::def_id::DefId;
-use rustc_target::abi::{Align, Size};
+use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi as CallAbi;
 
-use crate::const_eval::CheckAlignment;
-
 use super::{
     AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx,
     InterpResult, MPlaceTy, MemoryKind, OpTy, PlaceTy, Pointer, Provenance,
@@ -134,7 +133,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
     const POST_MONO_CHECKS: bool = true;
 
     /// Whether memory accesses should be alignment-checked.
-    fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment;
+    fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
     /// Whether, when checking alignment, we should look at the actual address and thus support
     /// custom alignment logic based on whatever the integer address happens to be.
@@ -142,13 +141,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
     /// If this returns true, Provenance::OFFSET_IS_ADDR must be true.
     fn use_addr_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
-    fn alignment_check_failed(
-        ecx: &InterpCx<'mir, 'tcx, Self>,
-        has: Align,
-        required: Align,
-        check: CheckAlignment,
-    ) -> InterpResult<'tcx, ()>;
-
     /// Whether to enforce the validity invariant for a specific layout.
     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool;
 
@@ -240,6 +232,16 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
         right: &ImmTy<'tcx, Self::Provenance>,
     ) -> InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)>;
 
+    /// Generate the NaN returned by a float operation, given the list of inputs.
+    /// (This is all inputs, not just NaN inputs!)
+    fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
+        _ecx: &InterpCx<'mir, 'tcx, Self>,
+        _inputs: &[F1],
+    ) -> F2 {
+        // By default we always return the preferred NaN.
+        F2::NAN
+    }
+
     /// Called before writing the specified `local` of the `frame`.
     /// Since writing a ZST is not actually accessing memory or locals, this is never invoked
     /// for ZST reads.
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index ce666e6af3b..edd8c540226 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -18,7 +18,6 @@ use rustc_middle::mir::display_allocation;
 use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
 use rustc_target::abi::{Align, HasDataLayout, Size};
 
-use crate::const_eval::CheckAlignment;
 use crate::fluent_generated as fluent;
 
 use super::{
@@ -373,8 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         self.check_and_deref_ptr(
             ptr,
             size,
-            align,
-            M::enforce_alignment(self),
+            M::enforce_alignment(self).then_some(align),
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, prov| {
                 let (size, align) = self
@@ -395,17 +393,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         align: Align,
         msg: CheckInAllocMsg,
     ) -> InterpResult<'tcx> {
-        self.check_and_deref_ptr(
-            ptr,
-            size,
-            align,
-            CheckAlignment::Error,
-            msg,
-            |alloc_id, _, _| {
-                let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
-                Ok((size, align, ()))
-            },
-        )?;
+        self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| {
+            let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
+            Ok((size, align, ()))
+        })?;
         Ok(())
     }
 
@@ -419,8 +410,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         ptr: Pointer<Option<M::Provenance>>,
         size: Size,
-        align: Align,
-        check: CheckAlignment,
+        align: Option<Align>,
         msg: CheckInAllocMsg,
         alloc_size: impl FnOnce(
             AllocId,
@@ -436,8 +426,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     throw_ub!(DanglingIntPointer(addr, msg));
                 }
                 // Must be aligned.
-                if check.should_check() {
-                    self.check_offset_align(addr, align, check)?;
+                if let Some(align) = align {
+                    self.check_offset_align(addr, align)?;
                 }
                 None
             }
@@ -460,16 +450,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 }
                 // Test align. Check this last; if both bounds and alignment are violated
                 // we want the error to be about the bounds.
-                if check.should_check() {
+                if let Some(align) = align {
                     if M::use_addr_for_alignment_check(self) {
                         // `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
-                        self.check_offset_align(ptr.addr().bytes(), align, check)?;
+                        self.check_offset_align(ptr.addr().bytes(), align)?;
                     } else {
                         // Check allocation alignment and offset alignment.
                         if alloc_align.bytes() < align.bytes() {
-                            M::alignment_check_failed(self, alloc_align, align, check)?;
+                            throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
                         }
-                        self.check_offset_align(offset.bytes(), align, check)?;
+                        self.check_offset_align(offset.bytes(), align)?;
                     }
                 }
 
@@ -480,18 +470,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         })
     }
 
-    fn check_offset_align(
-        &self,
-        offset: u64,
-        align: Align,
-        check: CheckAlignment,
-    ) -> InterpResult<'tcx> {
+    fn check_offset_align(&self, offset: u64, align: Align) -> InterpResult<'tcx> {
         if offset % align.bytes() == 0 {
             Ok(())
         } else {
             // The biggest power of two through which `offset` is divisible.
             let offset_pow2 = 1 << offset.trailing_zeros();
-            M::alignment_check_failed(self, Align::from_bytes(offset_pow2).unwrap(), align, check)
+            throw_ub!(AlignmentCheckFailed {
+                has: Align::from_bytes(offset_pow2).unwrap(),
+                required: align
+            });
         }
     }
 }
@@ -609,8 +597,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let ptr_and_alloc = self.check_and_deref_ptr(
             ptr,
             size,
-            align,
-            M::enforce_alignment(self),
+            M::enforce_alignment(self).then_some(align),
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, prov| {
                 let alloc = self.get_alloc_raw(alloc_id)?;
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index b084864f3a7..53e1756d897 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -1,4 +1,4 @@
-use rustc_apfloat::Float;
+use rustc_apfloat::{Float, FloatConvert};
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::TyAndLayout;
@@ -104,7 +104,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         (ImmTy::from_bool(res, *self.tcx), false)
     }
 
-    fn binary_float_op<F: Float + Into<Scalar<M::Provenance>>>(
+    fn binary_float_op<F: Float + FloatConvert<F> + Into<Scalar<M::Provenance>>>(
         &self,
         bin_op: mir::BinOp,
         layout: TyAndLayout<'tcx>,
@@ -113,6 +113,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> (ImmTy<'tcx, M::Provenance>, bool) {
         use rustc_middle::mir::BinOp::*;
 
+        // Performs appropriate non-deterministic adjustments of NaN results.
+        let adjust_nan = |f: F| -> F {
+            if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f }
+        };
+
         let val = match bin_op {
             Eq => ImmTy::from_bool(l == r, *self.tcx),
             Ne => ImmTy::from_bool(l != r, *self.tcx),
@@ -120,11 +125,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Le => ImmTy::from_bool(l <= r, *self.tcx),
             Gt => ImmTy::from_bool(l > r, *self.tcx),
             Ge => ImmTy::from_bool(l >= r, *self.tcx),
-            Add => ImmTy::from_scalar((l + r).value.into(), layout),
-            Sub => ImmTy::from_scalar((l - r).value.into(), layout),
-            Mul => ImmTy::from_scalar((l * r).value.into(), layout),
-            Div => ImmTy::from_scalar((l / r).value.into(), layout),
-            Rem => ImmTy::from_scalar((l % r).value.into(), layout),
+            Add => ImmTy::from_scalar(adjust_nan((l + r).value).into(), layout),
+            Sub => ImmTy::from_scalar(adjust_nan((l - r).value).into(), layout),
+            Mul => ImmTy::from_scalar(adjust_nan((l * r).value).into(), layout),
+            Div => ImmTy::from_scalar(adjust_nan((l / r).value).into(), layout),
+            Rem => ImmTy::from_scalar(adjust_nan((l % r).value).into(), layout),
             _ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op),
         };
         (val, false)
@@ -456,6 +461,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 Ok((ImmTy::from_bool(res, *self.tcx), false))
             }
             ty::Float(fty) => {
+                // No NaN adjustment here, `-` is a bitwise operation!
                 let res = match (un_op, fty) {
                     (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
                     (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 79448f07cae..9f95d2a3246 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -500,8 +500,7 @@ where
             .size_and_align_of_mplace(&mplace)?
             .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
         // Due to packed places, only `mplace.align` matters.
-        let align =
-            if M::enforce_alignment(self).should_check() { mplace.align } else { Align::ONE };
+        let align = if M::enforce_alignment(self) { mplace.align } else { Align::ONE };
         self.check_ptr_access_align(mplace.ptr(), size, align, CheckInAllocMsg::DerefTest)?;
         Ok(())
     }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index f8f9bfb0470..90819d492f4 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -9,16 +9,17 @@ use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::traits::BuiltinImplSource;
+use rustc_middle::ty::GenericArgs;
 use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
-use rustc_middle::ty::{GenericArgKind, GenericArgs};
 use rustc_middle::ty::{TraitRef, TypeVisitableExt};
 use rustc_mir_dataflow::{self, Analysis};
 use rustc_span::{sym, Span, Symbol};
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
+use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
 
 use std::mem;
-use std::ops::Deref;
+use std::ops::{ControlFlow, Deref};
 
 use super::ops::{self, NonConstOp, Status};
 use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
@@ -188,6 +189,24 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
     }
 }
 
+struct LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
+    kind: LocalKind,
+    checker: &'ck mut Checker<'mir, 'tcx>,
+}
+
+impl<'ck, 'mir, 'tcx> TypeVisitor<TyCtxt<'tcx>> for LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        match t.kind() {
+            ty::FnPtr(_) => ControlFlow::Continue(()),
+            ty::Ref(_, _, hir::Mutability::Mut) => {
+                self.checker.check_op(ops::ty::MutRef(self.kind));
+                t.super_visit_with(self)
+            }
+            _ => t.super_visit_with(self),
+        }
+    }
+}
+
 pub struct Checker<'mir, 'tcx> {
     ccx: &'mir ConstCx<'mir, 'tcx>,
     qualifs: Qualifs<'mir, 'tcx>,
@@ -346,20 +365,9 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
     fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) {
         let kind = self.body.local_kind(local);
 
-        for ty in ty.walk() {
-            let ty = match ty.unpack() {
-                GenericArgKind::Type(ty) => ty,
-
-                // No constraints on lifetimes or constants, except potentially
-                // constants' types, but `walk` will get to them as well.
-                GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
-            };
+        let mut visitor = LocalReturnTyVisitor { kind, checker: self };
 
-            match *ty.kind() {
-                ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef(kind)),
-                _ => {}
-            }
-        }
+        visitor.visit_ty(ty);
     }
 
     fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
index 9685ad24a97..5dd414cfd41 100644
--- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
@@ -349,7 +349,7 @@ struct Inner<N: Idx> {
     post_order_rank: IndexVec<N, usize>,
     // Even though we track only the immediate dominator of each node, it's
     // possible to get its full list of dominators by looking up the dominator
-    // of each dominator. (See the `impl Iterator for Iter` definition).
+    // of each dominator.
     immediate_dominators: IndexVec<N, Option<N>>,
     time: IndexVec<N, Time>,
 }
@@ -377,13 +377,6 @@ impl<Node: Idx> Dominators<Node> {
         }
     }
 
-    /// Provides an iterator over each dominator up the CFG, for the given Node.
-    /// See the `impl Iterator for Iter` definition to understand how this works.
-    pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
-        assert!(self.is_reachable(node), "node {node:?} is not reachable");
-        Iter { dom_tree: self, node: Some(node) }
-    }
-
     /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
     /// relationship, the dominator will always precede the dominated. (The relative ordering
     /// of two unrelated nodes will also be consistent, but otherwise the order has no
@@ -413,24 +406,6 @@ impl<Node: Idx> Dominators<Node> {
     }
 }
 
-pub struct Iter<'dom, Node: Idx> {
-    dom_tree: &'dom Dominators<Node>,
-    node: Option<Node>,
-}
-
-impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
-    type Item = Node;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(node) = self.node {
-            self.node = self.dom_tree.immediate_dominator(node);
-            Some(node)
-        } else {
-            None
-        }
-    }
-}
-
 /// Describes the number of vertices discovered at the time when processing of a particular vertex
 /// started and when it finished. Both values are zero for unreachable vertices.
 #[derive(Copy, Clone, Default, Debug)]
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 1b3f39e69e1..7a45ac10f0b 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -5,10 +5,13 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
-#![feature(lazy_cell)]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(decl_macro)]
-#![feature(panic_update_hook)]
+#![feature(lazy_cell)]
 #![feature(let_chains)]
+#![feature(panic_update_hook)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 #![deny(rustc::untranslatable_diagnostic)]
@@ -392,7 +395,7 @@ fn run_compiler(
                 if ppm.needs_ast_map() {
                     queries.global_ctxt()?.enter(|tcx| {
                         tcx.ensure().early_lint_checks(());
-                        pretty::print_after_hir_lowering(tcx, *ppm);
+                        pretty::print(sess, *ppm, pretty::PrintExtra::NeedsAstMap { tcx });
                         Ok(())
                     })?;
 
@@ -401,7 +404,7 @@ fn run_compiler(
                     queries.global_ctxt()?.enter(|tcx| tcx.output_filenames(()));
                 } else {
                     let krate = queries.parse()?.steal();
-                    pretty::print_after_parsing(sess, &krate, *ppm);
+                    pretty::print(sess, *ppm, pretty::PrintExtra::AfterParsing { krate });
                 }
                 trace!("finished pretty-printing");
                 return early_exit();
@@ -542,7 +545,7 @@ pub enum Compilation {
 }
 
 impl Compilation {
-    pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
+    fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
         match self {
             Compilation::Stop => Compilation::Stop,
             Compilation::Continue => next(),
@@ -654,7 +657,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) {
     }
 }
 
-pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
+fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
     if sess.opts.unstable_opts.link_only {
         if let Input::File(file) = &sess.io.input {
             let outputs = compiler.build_output_filenames(sess, &[]);
@@ -695,7 +698,7 @@ pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Comp
     }
 }
 
-pub fn list_metadata(
+fn list_metadata(
     handler: &EarlyErrorHandler,
     sess: &Session,
     metadata_loader: &dyn MetadataLoader,
@@ -1181,7 +1184,7 @@ fn print_flag_list<T>(
 ///
 /// So with all that in mind, the comments below have some more detail about the
 /// contortions done here to get things to work out correctly.
-pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
+fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
     if args.is_empty() {
         // user did not write `-v` nor `-Z unstable-options`, so do not
         // include that extra information.
@@ -1280,9 +1283,9 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
     }
 }
 
-pub static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
+static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
 
-pub fn ice_path() -> &'static Option<PathBuf> {
+fn ice_path() -> &'static Option<PathBuf> {
     ICE_PATH.get_or_init(|| {
         if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
             return None;
@@ -1391,7 +1394,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
 ///
 /// When `install_ice_hook` is called, this function will be called as the panic
 /// hook.
-pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) {
+fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) {
     let fallback_bundle =
         rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
     let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index 222c7b5d6a7..8c6fee83013 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -1,14 +1,13 @@
 //! The various pretty-printing routines.
 
 use rustc_ast as ast;
-use rustc_ast_pretty::pprust;
-use rustc_errors::ErrorGuaranteed;
+use rustc_ast_pretty::pprust as pprust_ast;
 use rustc_hir as hir;
 use rustc_hir_pretty as pprust_hir;
-use rustc_middle::hir::map as hir_map;
+use rustc_middle::bug;
 use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::config::{OutFileName, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
+use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::FileName;
@@ -20,174 +19,57 @@ pub use self::PpMode::*;
 pub use self::PpSourceMode::*;
 use crate::abort_on_err;
 
-// This slightly awkward construction is to allow for each PpMode to
-// choose whether it needs to do analyses (which can consume the
-// Session) and then pass through the session (now attached to the
-// analysis results) on to the chosen pretty-printer, along with the
-// `&PpAnn` object.
-//
-// Note that since the `&PrinterSupport` is freshly constructed on each
-// call, it would not make sense to try to attach the lifetime of `self`
-// to the lifetime of the `&PrinterObject`.
-
-/// Constructs a `PrinterSupport` object and passes it to `f`.
-fn call_with_pp_support<'tcx, A, F>(
-    ppmode: &PpSourceMode,
-    sess: &'tcx Session,
-    tcx: Option<TyCtxt<'tcx>>,
-    f: F,
-) -> A
-where
-    F: FnOnce(&dyn PrinterSupport) -> A,
-{
-    match *ppmode {
-        Normal | Expanded => {
-            let annotation = NoAnn { sess, tcx };
-            f(&annotation)
-        }
-
-        Identified | ExpandedIdentified => {
-            let annotation = IdentifiedAnnotation { sess, tcx };
-            f(&annotation)
-        }
-        ExpandedHygiene => {
-            let annotation = HygieneAnnotation { sess };
-            f(&annotation)
-        }
-    }
-}
-fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A
-where
-    F: FnOnce(&dyn HirPrinterSupport<'_>, hir_map::Map<'_>) -> A,
-{
-    match *ppmode {
-        PpHirMode::Normal => {
-            let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
-            f(&annotation, tcx.hir())
-        }
-
-        PpHirMode::Identified => {
-            let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
-            f(&annotation, tcx.hir())
-        }
-        PpHirMode::Typed => {
-            abort_on_err(tcx.analysis(()), tcx.sess);
-
-            let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) };
-            tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir()))
-        }
-    }
-}
-
-trait PrinterSupport: pprust::PpAnn {
-    /// Provides a uniform interface for re-extracting a reference to a
-    /// `Session` from a value that now owns it.
-    fn sess(&self) -> &Session;
-
-    /// Produces the pretty-print annotation object.
-    ///
-    /// (Rust does not yet support upcasting from a trait object to
-    /// an object for one of its supertraits.)
-    fn pp_ann(&self) -> &dyn pprust::PpAnn;
-}
-
-trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
-    /// Provides a uniform interface for re-extracting a reference to a
-    /// `Session` from a value that now owns it.
-    fn sess(&self) -> &Session;
-
-    /// Provides a uniform interface for re-extracting a reference to an
-    /// `hir_map::Map` from a value that now owns it.
-    fn hir_map(&self) -> Option<hir_map::Map<'hir>>;
-
-    /// Produces the pretty-print annotation object.
-    ///
-    /// (Rust does not yet support upcasting from a trait object to
-    /// an object for one of its supertraits.)
-    fn pp_ann(&self) -> &dyn pprust_hir::PpAnn;
-}
-
-struct NoAnn<'hir> {
-    sess: &'hir Session,
-    tcx: Option<TyCtxt<'hir>>,
-}
-
-impl<'hir> PrinterSupport for NoAnn<'hir> {
-    fn sess(&self) -> &Session {
-        self.sess
-    }
-
-    fn pp_ann(&self) -> &dyn pprust::PpAnn {
-        self
-    }
-}
+struct AstNoAnn;
 
-impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
-    fn sess(&self) -> &Session {
-        self.sess
-    }
-
-    fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
-        self.tcx.map(|tcx| tcx.hir())
-    }
+impl pprust_ast::PpAnn for AstNoAnn {}
 
-    fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
-        self
-    }
+struct HirNoAnn<'tcx> {
+    tcx: TyCtxt<'tcx>,
 }
 
-impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
-impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
+impl<'tcx> pprust_hir::PpAnn for HirNoAnn<'tcx> {
     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
-        if let Some(tcx) = self.tcx {
-            pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
-        }
+        pprust_hir::PpAnn::nested(
+            &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>),
+            state,
+            nested,
+        )
     }
 }
 
-struct IdentifiedAnnotation<'hir> {
-    sess: &'hir Session,
-    tcx: Option<TyCtxt<'hir>>,
-}
-
-impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
-    fn sess(&self) -> &Session {
-        self.sess
-    }
+struct AstIdentifiedAnn;
 
-    fn pp_ann(&self) -> &dyn pprust::PpAnn {
-        self
-    }
-}
-
-impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
-    fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
-        if let pprust::AnnNode::Expr(_) = node {
+impl pprust_ast::PpAnn for AstIdentifiedAnn {
+    fn pre(&self, s: &mut pprust_ast::State<'_>, node: pprust_ast::AnnNode<'_>) {
+        if let pprust_ast::AnnNode::Expr(_) = node {
             s.popen();
         }
     }
-    fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
+
+    fn post(&self, s: &mut pprust_ast::State<'_>, node: pprust_ast::AnnNode<'_>) {
         match node {
-            pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {}
+            pprust_ast::AnnNode::Crate(_)
+            | pprust_ast::AnnNode::Ident(_)
+            | pprust_ast::AnnNode::Name(_) => {}
 
-            pprust::AnnNode::Item(item) => {
+            pprust_ast::AnnNode::Item(item) => {
                 s.s.space();
                 s.synth_comment(item.id.to_string())
             }
-            pprust::AnnNode::SubItem(id) => {
+            pprust_ast::AnnNode::SubItem(id) => {
                 s.s.space();
                 s.synth_comment(id.to_string())
             }
-            pprust::AnnNode::Block(blk) => {
+            pprust_ast::AnnNode::Block(blk) => {
                 s.s.space();
                 s.synth_comment(format!("block {}", blk.id))
             }
-            pprust::AnnNode::Expr(expr) => {
+            pprust_ast::AnnNode::Expr(expr) => {
                 s.s.space();
                 s.synth_comment(expr.id.to_string());
                 s.pclose()
             }
-            pprust::AnnNode::Pat(pat) => {
+            pprust_ast::AnnNode::Pat(pat) => {
                 s.s.space();
                 s.synth_comment(format!("pat {}", pat.id));
             }
@@ -195,31 +77,25 @@ impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
     }
 }
 
-impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
-    fn sess(&self) -> &Session {
-        self.sess
-    }
-
-    fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
-        self.tcx.map(|tcx| tcx.hir())
-    }
-
-    fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
-        self
-    }
+struct HirIdentifiedAnn<'tcx> {
+    tcx: TyCtxt<'tcx>,
 }
 
-impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
+impl<'tcx> pprust_hir::PpAnn for HirIdentifiedAnn<'tcx> {
     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
-        if let Some(ref tcx) = self.tcx {
-            pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
-        }
+        pprust_hir::PpAnn::nested(
+            &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>),
+            state,
+            nested,
+        )
     }
+
     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
         if let pprust_hir::AnnNode::Expr(_) = node {
             s.popen();
         }
     }
+
     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
         match node {
             pprust_hir::AnnNode::Name(_) => {}
@@ -252,32 +128,22 @@ impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
     }
 }
 
-struct HygieneAnnotation<'a> {
+struct AstHygieneAnn<'a> {
     sess: &'a Session,
 }
 
-impl<'a> PrinterSupport for HygieneAnnotation<'a> {
-    fn sess(&self) -> &Session {
-        self.sess
-    }
-
-    fn pp_ann(&self) -> &dyn pprust::PpAnn {
-        self
-    }
-}
-
-impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
-    fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
+impl<'a> pprust_ast::PpAnn for AstHygieneAnn<'a> {
+    fn post(&self, s: &mut pprust_ast::State<'_>, node: pprust_ast::AnnNode<'_>) {
         match node {
-            pprust::AnnNode::Ident(&Ident { name, span }) => {
+            pprust_ast::AnnNode::Ident(&Ident { name, span }) => {
                 s.s.space();
                 s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
             }
-            pprust::AnnNode::Name(&name) => {
+            pprust_ast::AnnNode::Name(&name) => {
                 s.s.space();
                 s.synth_comment(name.as_u32().to_string())
             }
-            pprust::AnnNode::Crate(_) => {
+            pprust_ast::AnnNode::Crate(_) => {
                 s.s.hardbreak();
                 let verbose = self.sess.verbose();
                 s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose));
@@ -288,26 +154,12 @@ impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
     }
 }
 
-struct TypedAnnotation<'tcx> {
+struct HirTypedAnn<'tcx> {
     tcx: TyCtxt<'tcx>,
     maybe_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
 }
 
-impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> {
-    fn sess(&self) -> &Session {
-        self.tcx.sess
-    }
-
-    fn hir_map(&self) -> Option<hir_map::Map<'tcx>> {
-        Some(self.tcx.hir())
-    }
-
-    fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
-        self
-    }
-}
-
-impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
+impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> {
     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
         let old_maybe_typeck_results = self.maybe_typeck_results.get();
         if let pprust_hir::Nested::Body(id) = nested {
@@ -317,11 +169,13 @@ impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
         pprust_hir::PpAnn::nested(pp_ann, state, nested);
         self.maybe_typeck_results.set(old_maybe_typeck_results);
     }
+
     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
         if let pprust_hir::AnnNode::Expr(_) = node {
             s.popen();
         }
     }
+
     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
         if let pprust_hir::AnnNode::Expr(expr) = node {
             let typeck_results = self.maybe_typeck_results.get().or_else(|| {
@@ -360,119 +214,119 @@ fn write_or_print(out: &str, sess: &Session) {
     sess.io.output_file.as_ref().unwrap_or(&OutFileName::Stdout).overwrite(out, sess);
 }
 
-pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) {
-    let (src, src_name) = get_source(sess);
+// Extra data for pretty-printing, the form of which depends on what kind of
+// pretty-printing we are doing.
+pub enum PrintExtra<'tcx> {
+    AfterParsing { krate: ast::Crate },
+    NeedsAstMap { tcx: TyCtxt<'tcx> },
+}
 
-    let out = match ppm {
-        Source(s) => {
-            // Silently ignores an identified node.
-            call_with_pp_support(&s, sess, None, move |annotation| {
-                debug!("pretty printing source code {:?}", s);
-                let sess = annotation.sess();
-                let parse = &sess.parse_sess;
-                pprust::print_crate(
-                    sess.source_map(),
-                    krate,
-                    src_name,
-                    src,
-                    annotation.pp_ann(),
-                    false,
-                    parse.edition,
-                    &sess.parse_sess.attr_id_generator,
-                )
-            })
+impl<'tcx> PrintExtra<'tcx> {
+    fn with_krate<F, R>(&self, f: F) -> R
+    where
+        F: FnOnce(&ast::Crate) -> R,
+    {
+        match self {
+            PrintExtra::AfterParsing { krate, .. } => f(krate),
+            PrintExtra::NeedsAstMap { tcx } => f(&tcx.resolver_for_lowering(()).borrow().1),
         }
-        AstTree(PpAstTreeMode::Normal) => {
-            debug!("pretty printing AST tree");
-            format!("{krate:#?}")
-        }
-        _ => unreachable!(),
-    };
+    }
 
-    write_or_print(&out, sess);
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        match self {
+            PrintExtra::AfterParsing { .. } => bug!("PrintExtra::tcx"),
+            PrintExtra::NeedsAstMap { tcx } => *tcx,
+        }
+    }
 }
 
-pub fn print_after_hir_lowering<'tcx>(tcx: TyCtxt<'tcx>, ppm: PpMode) {
+pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
     if ppm.needs_analysis() {
-        abort_on_err(print_with_analysis(tcx, ppm), tcx.sess);
-        return;
+        abort_on_err(ex.tcx().analysis(()), sess);
     }
 
-    let (src, src_name) = get_source(tcx.sess);
+    let (src, src_name) = get_source(sess);
 
     let out = match ppm {
         Source(s) => {
-            // Silently ignores an identified node.
-            call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
-                debug!("pretty printing source code {:?}", s);
-                let sess = annotation.sess();
-                let parse = &sess.parse_sess;
-                pprust::print_crate(
+            debug!("pretty printing source code {:?}", s);
+            let annotation: Box<dyn pprust_ast::PpAnn> = match s {
+                Normal => Box::new(AstNoAnn),
+                Expanded => Box::new(AstNoAnn),
+                Identified => Box::new(AstIdentifiedAnn),
+                ExpandedIdentified => Box::new(AstIdentifiedAnn),
+                ExpandedHygiene => Box::new(AstHygieneAnn { sess }),
+            };
+            let parse = &sess.parse_sess;
+            let is_expanded = ppm.needs_ast_map();
+            ex.with_krate(|krate| {
+                pprust_ast::print_crate(
                     sess.source_map(),
-                    &tcx.resolver_for_lowering(()).borrow().1,
+                    krate,
                     src_name,
                     src,
-                    annotation.pp_ann(),
-                    true,
+                    &*annotation,
+                    is_expanded,
                     parse.edition,
                     &sess.parse_sess.attr_id_generator,
                 )
             })
         }
-
-        AstTree(PpAstTreeMode::Expanded) => {
+        AstTree => {
+            debug!("pretty printing AST tree");
+            ex.with_krate(|krate| format!("{krate:#?}"))
+        }
+        AstTreeExpanded => {
             debug!("pretty-printing expanded AST");
-            format!("{:#?}", tcx.resolver_for_lowering(()).borrow().1)
+            format!("{:#?}", ex.tcx().resolver_for_lowering(()).borrow().1)
         }
-
-        Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| {
+        Hir(s) => {
             debug!("pretty printing HIR {:?}", s);
-            let sess = annotation.sess();
-            let sm = sess.source_map();
-            let attrs = |id| hir_map.attrs(id);
-            pprust_hir::print_crate(
-                sm,
-                hir_map.root_module(),
-                src_name,
-                src,
-                &attrs,
-                annotation.pp_ann(),
-            )
-        }),
-
+            let tcx = ex.tcx();
+            let f = |annotation: &dyn pprust_hir::PpAnn| {
+                let sm = sess.source_map();
+                let hir_map = tcx.hir();
+                let attrs = |id| hir_map.attrs(id);
+                pprust_hir::print_crate(
+                    sm,
+                    hir_map.root_module(),
+                    src_name,
+                    src,
+                    &attrs,
+                    annotation,
+                )
+            };
+            match s {
+                PpHirMode::Normal => {
+                    let annotation = HirNoAnn { tcx };
+                    f(&annotation)
+                }
+                PpHirMode::Identified => {
+                    let annotation = HirIdentifiedAnn { tcx };
+                    f(&annotation)
+                }
+                PpHirMode::Typed => {
+                    let annotation = HirTypedAnn { tcx, maybe_typeck_results: Cell::new(None) };
+                    tcx.dep_graph.with_ignore(|| f(&annotation))
+                }
+            }
+        }
         HirTree => {
-            call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, hir_map| {
-                debug!("pretty printing HIR tree");
-                format!("{:#?}", hir_map.krate())
-            })
+            debug!("pretty printing HIR tree");
+            format!("{:#?}", ex.tcx().hir().krate())
         }
-
-        _ => unreachable!(),
-    };
-
-    write_or_print(&out, tcx.sess);
-}
-
-// In an ideal world, this would be a public function called by the driver after
-// analysis is performed. However, we want to call `phase_3_run_analysis_passes`
-// with a different callback than the standard driver, so that isn't easy.
-// Instead, we call that function ourselves.
-fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuaranteed> {
-    tcx.analysis(())?;
-    let out = match ppm {
         Mir => {
             let mut out = Vec::new();
-            write_mir_pretty(tcx, None, &mut out).unwrap();
+            write_mir_pretty(ex.tcx(), None, &mut out).unwrap();
             String::from_utf8(out).unwrap()
         }
-
         MirCFG => {
             let mut out = Vec::new();
-            write_mir_graphviz(tcx, None, &mut out).unwrap();
+            write_mir_graphviz(ex.tcx(), None, &mut out).unwrap();
             String::from_utf8(out).unwrap()
         }
-
         ThirTree => {
+            let tcx = ex.tcx();
             let mut out = String::new();
             abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
             debug!("pretty printing THIR tree");
@@ -481,8 +335,8 @@ fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuarante
             }
             out
         }
-
         ThirFlat => {
+            let tcx = ex.tcx();
             let mut out = String::new();
             abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
             debug!("pretty printing THIR flat");
@@ -491,11 +345,7 @@ fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuarante
             }
             out
         }
-
-        _ => unreachable!(),
     };
 
-    write_or_print(&out, tcx.sess);
-
-    Ok(())
+    write_or_print(&out, sess);
 }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0706.md b/compiler/rustc_error_codes/src/error_codes/E0706.md
index fabd855a222..a09abb59ba8 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0706.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0706.md
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 `async fn`s are not yet supported in traits in Rust.
 
 Erroneous code example:
 
-```compile_fail,edition2018
+```ignore,edition2018
 trait T {
     // Neither case is currently supported.
     async fn foo() {}
@@ -13,7 +15,7 @@ trait T {
 `async fn`s return an `impl Future`, making the following two examples
 equivalent:
 
-```edition2018,ignore (example-of-desugaring-equivalence)
+```ignore,edition2018 (example-of-desugaring-equivalence)
 async fn foo() -> User {
     unimplemented!()
 }
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 5e23ae655fe..85acf8ab5aa 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -659,6 +659,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self);
     forward!(pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
+    forward!(pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
     forward!(pub fn span_help(
         &mut self,
         sp: impl Into<MultiSpan>,
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 665b5d6adec..63226504d37 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -3,6 +3,8 @@
 //! This module contains the code for creating and emitting diagnostics.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(array_windows)]
 #![feature(extract_if)]
 #![feature(if_let_guard)]
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 32d8380abd3..b198876b05c 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -67,6 +67,8 @@ declare_features! (
     (accepted, associated_types, "1.0.0", None, None),
     /// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions.
     (accepted, async_await, "1.39.0", Some(50547), None),
+    /// Allows async functions to be declared, implemented, and used in traits.
+    (accepted, async_fn_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None),
     /// Allows all literals in attribute lists and values of key-value pairs.
     (accepted, attr_literals, "1.30.0", Some(34981), None),
     /// Allows overloading augmented assignment operations like `a += b`.
@@ -198,7 +200,7 @@ declare_features! (
     /// + `impl Debug for Foo<'_>`
     (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None),
     /// Allows referencing `Self` and projections in impl-trait.
-    (accepted, impl_trait_projections, "CURRENT_RUSTC_VERSION", Some(103532), None),
+    (accepted, impl_trait_projections, "1.74.0", Some(103532), None),
     /// Allows using `a..=b` and `..=b` as inclusive range syntaxes.
     (accepted, inclusive_range_syntax, "1.26.0", Some(28237), None),
     /// Allows inferring outlives requirements (RFC 2093).
@@ -270,7 +272,7 @@ declare_features! (
     /// Allows the use of or-patterns (e.g., `0 | 1`).
     (accepted, or_patterns, "1.53.0", Some(54883), None),
     /// Allows using `+bundle,+whole-archive` link modifiers with native libs.
-    (accepted, packed_bundled_libs, "CURRENT_RUSTC_VERSION", Some(108081), None),
+    (accepted, packed_bundled_libs, "1.74.0", Some(108081), None),
     /// Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`.
     /// This defines the behavior of panics.
     (accepted, panic_handler, "1.30.0", Some(44489), None),
@@ -306,6 +308,8 @@ declare_features! (
     (accepted, repr_packed, "1.33.0", Some(33158), None),
     /// Allows `#[repr(transparent)]` attribute on newtype structs.
     (accepted, repr_transparent, "1.28.0", Some(43036), None),
+    /// Allows return-position `impl Trait` in traits.
+    (accepted, return_position_impl_trait_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None),
     /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414).
     (accepted, rvalue_static_promotion, "1.21.0", Some(38865), None),
     /// Allows `Self` in type definitions (RFC 2300).
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 83961647bd4..ef672487b98 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -351,8 +351,6 @@ declare_features! (
     (active, associated_type_defaults, "1.2.0", Some(29661), None),
     /// Allows `async || body` closures.
     (active, async_closure, "1.37.0", Some(62290), None),
-    /// Allows async functions to be declared, implemented, and used in traits.
-    (active, async_fn_in_trait, "1.66.0", Some(91611), None),
     /// Allows `#[track_caller]` on async functions.
     (active, async_fn_track_caller, "1.73.0", Some(110011), None),
     /// Allows builtin # foo() syntax
@@ -413,9 +411,9 @@ declare_features! (
     (active, const_try, "1.56.0", Some(74935), None),
     /// Allows function attribute `#[coverage(on/off)]`, to control coverage
     /// instrumentation of that function.
-    (active, coverage_attribute, "CURRENT_RUSTC_VERSION", Some(84605), None),
+    (active, coverage_attribute, "1.74.0", Some(84605), None),
     /// Allows users to provide classes for fenced code block using `class:classname`.
-    (active, custom_code_classes_in_docs, "CURRENT_RUSTC_VERSION", Some(79483), None),
+    (active, custom_code_classes_in_docs, "1.74.0", Some(79483), None),
     /// Allows non-builtin attributes in inner attribute position.
     (active, custom_inner_attributes, "1.30.0", Some(54726), None),
     /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
@@ -551,8 +549,6 @@ declare_features! (
     (incomplete, repr128, "1.16.0", Some(56071), None),
     /// Allows `repr(simd)` and importing the various simd intrinsics.
     (active, repr_simd, "1.4.0", Some(27731), None),
-    /// Allows return-position `impl Trait` in traits.
-    (active, return_position_impl_trait_in_trait, "1.65.0", Some(91611), None),
     /// Allows bounding the return type of AFIT/RPITIT.
     (incomplete, return_type_notation, "1.70.0", Some(109417), None),
     /// Allows `extern "rust-cold"`.
@@ -596,7 +592,7 @@ declare_features! (
     /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
     (active, unix_sigpipe, "1.65.0", Some(97889), None),
     /// Allows unnamed fields of struct and union type
-    (incomplete, unnamed_fields, "CURRENT_RUSTC_VERSION", Some(49804), None),
+    (incomplete, unnamed_fields, "1.74.0", Some(49804), None),
     /// Allows unsized fn parameters.
     (active, unsized_fn_params, "1.49.0", Some(48055), None),
     /// Allows unsized rvalues at arguments and parameters.
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index de15deef178..1697e929eda 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -121,7 +121,7 @@ declare_features! (
     (removed, negate_unsigned, "1.0.0", Some(29645), None, None),
     /// Allows `#[no_coverage]` on functions.
     /// The feature was renamed to `coverage_attribute` and the attribute to `#[coverage(on|off)]`
-    (removed, no_coverage, "CURRENT_RUSTC_VERSION", Some(84605), None, Some("renamed to `coverage_attribute`")),
+    (removed, no_coverage, "1.74.0", Some(84605), None, Some("renamed to `coverage_attribute`")),
     /// Allows `#[no_debug]`.
     (removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")),
     /// Note: this feature was previously recorded in a separate
diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs
index a01643cd67d..191fb787f70 100644
--- a/compiler/rustc_fluent_macro/src/lib.rs
+++ b/compiler/rustc_fluent_macro/src/lib.rs
@@ -1,4 +1,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(proc_macro_diagnostic)]
 #![feature(proc_macro_span)]
 #![deny(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs
index 5d86d895817..9cb279e3efd 100644
--- a/compiler/rustc_graphviz/src/lib.rs
+++ b/compiler/rustc_graphviz/src/lib.rs
@@ -273,6 +273,9 @@
     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(attr(allow(unused_variables), deny(warnings)))
 )]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 56b1fd36973..a91d9231390 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -36,7 +36,7 @@ use rustc_middle::ty::{
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{sym, BytePos, Span, DUMMY_SP};
 use rustc_target::spec::abi;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCtxt};
@@ -1275,8 +1275,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                                 return;
                             };
                             // Get the span of the generics args *including* the leading `::`.
-                            let args_span =
-                                assoc_segment.ident.span.shrink_to_hi().to(args.span_ext);
+                            // We do so by stretching args.span_ext to the left by 2. Earlier
+                            // it was done based on the end of assoc segment but that sometimes
+                            // led to impossible spans and caused issues like #116473
+                            let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2));
                             if tcx.generics_of(adt_def.did()).count() == 0 {
                                 // FIXME(estebank): we could also verify that the arguments being
                                 // work for the `enum`, instead of just looking if it takes *any*.
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 486aac21972..87a30f9f813 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -783,21 +783,21 @@ fn check_impl_items_against_trait<'tcx>(
                 let (msg, feature) = if tcx.asyncness(def_id).is_async() {
                     (
                         format!("async {descr} in trait cannot be specialized"),
-                        sym::async_fn_in_trait,
+                        "async functions in traits",
                     )
                 } else {
                     (
                         format!(
                             "{descr} with return-position `impl Trait` in trait cannot be specialized"
                         ),
-                        sym::return_position_impl_trait_in_trait,
+                        "return position `impl Trait` in traits",
                     )
                 };
                 tcx.sess
                     .struct_span_err(tcx.def_span(def_id), msg)
                     .note(format!(
                         "specialization behaves in inconsistent and \
-                        surprising ways with `#![feature({feature})]`, \
+                        surprising ways with {feature}, \
                         and for now is disallowed"
                     ))
                     .emit();
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index f1514ecf69c..fc8e5a795fb 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -633,8 +633,6 @@ fn compare_asyncness<'tcx>(
 /// For example, given the sample code:
 ///
 /// ```
-/// #![feature(return_position_impl_trait_in_trait)]
-///
 /// use std::ops::Deref;
 ///
 /// trait Foo {
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 03963925d3d..c7b3648099c 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -57,6 +57,9 @@ This API is completely unstable and subject to change.
 
 #![allow(rustc::potential_query_instability)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(if_let_guard)]
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 8587b009f25..ab6ab391484 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -52,8 +52,6 @@ pub struct NoAnn;
 impl PpAnn for NoAnn {}
 pub const NO_ANN: &dyn PpAnn = &NoAnn;
 
-/// Identical to the `PpAnn` implementation for `hir::Crate`,
-/// except it avoids creating a dependency on the whole crate.
 impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> {
     fn nested(&self, state: &mut State<'_>, nested: Nested) {
         match nested {
@@ -75,7 +73,11 @@ pub struct State<'a> {
 }
 
 impl<'a> State<'a> {
-    pub fn print_node(&mut self, node: Node<'_>) {
+    fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] {
+        (self.attrs)(id)
+    }
+
+    fn print_node(&mut self, node: Node<'_>) {
         match node {
             Node::Param(a) => self.print_param(a),
             Node::Item(a) => self.print_item(a),
@@ -144,7 +146,7 @@ impl<'a> PrintState<'a> for State<'a> {
     }
 }
 
-pub const INDENT_UNIT: isize = 4;
+const INDENT_UNIT: isize = 4;
 
 /// Requires you to pass an input filename and reader so that
 /// it can scan the input text for comments to copy forward.
@@ -156,7 +158,12 @@ pub fn print_crate<'a>(
     attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute],
     ann: &'a dyn PpAnn,
 ) -> String {
-    let mut s = State::new_from_input(sm, filename, input, attrs, ann);
+    let mut s = State {
+        s: pp::Printer::new(),
+        comments: Some(Comments::new(sm, filename, input)),
+        attrs,
+        ann,
+    };
 
     // When printing the AST, we sometimes need to inject `#[no_std]` here.
     // Since you can't compile the HIR, it's not necessary.
@@ -166,28 +173,7 @@ pub fn print_crate<'a>(
     s.s.eof()
 }
 
-impl<'a> State<'a> {
-    pub fn new_from_input(
-        sm: &'a SourceMap,
-        filename: FileName,
-        input: String,
-        attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute],
-        ann: &'a dyn PpAnn,
-    ) -> State<'a> {
-        State {
-            s: pp::Printer::new(),
-            comments: Some(Comments::new(sm, filename, input)),
-            attrs,
-            ann,
-        }
-    }
-
-    fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] {
-        (self.attrs)(id)
-    }
-}
-
-pub fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
+fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
 where
     F: FnOnce(&mut State<'_>),
 {
@@ -196,52 +182,20 @@ where
     printer.s.eof()
 }
 
-pub fn generic_params_to_string(generic_params: &[GenericParam<'_>]) -> String {
-    to_string(NO_ANN, |s| s.print_generic_params(generic_params))
-}
-
-pub fn bounds_to_string<'b>(bounds: impl IntoIterator<Item = &'b hir::GenericBound<'b>>) -> String {
-    to_string(NO_ANN, |s| s.print_bounds("", bounds))
-}
-
 pub fn ty_to_string(ty: &hir::Ty<'_>) -> String {
     to_string(NO_ANN, |s| s.print_type(ty))
 }
 
-pub fn path_segment_to_string(segment: &hir::PathSegment<'_>) -> String {
-    to_string(NO_ANN, |s| s.print_path_segment(segment))
-}
-
-pub fn path_to_string(segment: &hir::Path<'_>) -> String {
-    to_string(NO_ANN, |s| s.print_path(segment, false))
-}
-
 pub fn qpath_to_string(segment: &hir::QPath<'_>) -> String {
     to_string(NO_ANN, |s| s.print_qpath(segment, false))
 }
 
-pub fn fn_to_string(
-    decl: &hir::FnDecl<'_>,
-    header: hir::FnHeader,
-    name: Option<Symbol>,
-    generics: &hir::Generics<'_>,
-    arg_names: &[Ident],
-    body_id: Option<hir::BodyId>,
-) -> String {
-    to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, arg_names, body_id))
-}
-
-pub fn enum_def_to_string(
-    enum_definition: &hir::EnumDef<'_>,
-    generics: &hir::Generics<'_>,
-    name: Symbol,
-    span: rustc_span::Span,
-) -> String {
-    to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span))
+pub fn pat_to_string(pat: &hir::Pat<'_>) -> String {
+    to_string(NO_ANN, |s| s.print_pat(pat))
 }
 
 impl<'a> State<'a> {
-    pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
+    fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
         self.maybe_print_comment(span.hi());
         self.break_offset_if_not_bol(1, -INDENT_UNIT);
         self.word("}");
@@ -250,11 +204,11 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn bclose(&mut self, span: rustc_span::Span) {
+    fn bclose(&mut self, span: rustc_span::Span) {
         self.bclose_maybe_open(span, true)
     }
 
-    pub fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
+    fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
     where
         F: FnMut(&mut State<'_>, &T),
         G: FnMut(&T) -> rustc_span::Span,
@@ -275,25 +229,25 @@ impl<'a> State<'a> {
         self.end();
     }
 
-    pub fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) {
+    fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) {
         self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span);
     }
 
-    pub fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
+    fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
         self.print_inner_attributes(attrs);
         for &item_id in _mod.item_ids {
             self.ann.nested(self, Nested::Item(item_id));
         }
     }
 
-    pub fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) {
+    fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) {
         if !lifetime.is_elided() {
             self.print_lifetime(lifetime);
             self.nbsp();
         }
     }
 
-    pub fn print_type(&mut self, ty: &hir::Ty<'_>) {
+    fn print_type(&mut self, ty: &hir::Ty<'_>) {
         self.maybe_print_comment(ty.span.lo());
         self.ibox(0);
         match ty.kind {
@@ -371,7 +325,7 @@ impl<'a> State<'a> {
         self.end()
     }
 
-    pub fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
+    fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(item.span.lo());
         self.print_outer_attributes(self.attrs(item.hir_id()));
@@ -478,8 +432,7 @@ impl<'a> State<'a> {
         self.end(); // end the outer ibox
     }
 
-    /// Pretty-print an item
-    pub fn print_item(&mut self, item: &hir::Item<'_>) {
+    fn print_item(&mut self, item: &hir::Item<'_>) {
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(item.span.lo());
         let attrs = self.attrs(item.hir_id());
@@ -704,7 +657,7 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::Item(item))
     }
 
-    pub fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) {
+    fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) {
         self.print_path(t.path, false);
     }
 
@@ -721,7 +674,7 @@ impl<'a> State<'a> {
         self.print_trait_ref(&t.trait_ref);
     }
 
-    pub fn print_enum_def(
+    fn print_enum_def(
         &mut self,
         enum_definition: &hir::EnumDef<'_>,
         generics: &hir::Generics<'_>,
@@ -736,7 +689,7 @@ impl<'a> State<'a> {
         self.print_variants(enum_definition.variants, span);
     }
 
-    pub fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) {
+    fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) {
         self.bopen();
         for v in variants {
             self.space_if_not_bol();
@@ -751,14 +704,14 @@ impl<'a> State<'a> {
         self.bclose(span)
     }
 
-    pub fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
+    fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
         match defaultness {
             hir::Defaultness::Default { .. } => self.word_nbsp("default"),
             hir::Defaultness::Final => (),
         }
     }
 
-    pub fn print_struct(
+    fn print_struct(
         &mut self,
         struct_def: &hir::VariantData<'_>,
         generics: &hir::Generics<'_>,
@@ -807,7 +760,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_variant(&mut self, v: &hir::Variant<'_>) {
+    fn print_variant(&mut self, v: &hir::Variant<'_>) {
         self.head("");
         let generics = hir::Generics::empty();
         self.print_struct(&v.data, generics, v.ident.name, v.span, false);
@@ -817,7 +770,8 @@ impl<'a> State<'a> {
             self.print_anon_const(d);
         }
     }
-    pub fn print_method_sig(
+
+    fn print_method_sig(
         &mut self,
         ident: Ident,
         m: &hir::FnSig<'_>,
@@ -828,7 +782,7 @@ impl<'a> State<'a> {
         self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_names, body_id);
     }
 
-    pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
+    fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
         self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(ti.span.lo());
@@ -856,7 +810,7 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::SubItem(ti.hir_id()))
     }
 
-    pub fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) {
+    fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) {
         self.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(ii.span.lo());
@@ -881,7 +835,7 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::SubItem(ii.hir_id()))
     }
 
-    pub fn print_local(
+    fn print_local(
         &mut self,
         init: Option<&hir::Expr<'_>>,
         els: Option<&hir::Block<'_>>,
@@ -914,7 +868,7 @@ impl<'a> State<'a> {
         self.end()
     }
 
-    pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
+    fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
         self.maybe_print_comment(st.span.lo());
         match st.kind {
             hir::StmtKind::Local(loc) => {
@@ -937,19 +891,19 @@ impl<'a> State<'a> {
         self.maybe_print_trailing_comment(st.span, None)
     }
 
-    pub fn print_block(&mut self, blk: &hir::Block<'_>) {
+    fn print_block(&mut self, blk: &hir::Block<'_>) {
         self.print_block_with_attrs(blk, &[])
     }
 
-    pub fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) {
+    fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) {
         self.print_block_maybe_unclosed(blk, &[], false)
     }
 
-    pub fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) {
+    fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) {
         self.print_block_maybe_unclosed(blk, attrs, true)
     }
 
-    pub fn print_block_maybe_unclosed(
+    fn print_block_maybe_unclosed(
         &mut self,
         blk: &hir::Block<'_>,
         attrs: &[ast::Attribute],
@@ -1005,7 +959,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_if(
+    fn print_if(
         &mut self,
         test: &hir::Expr<'_>,
         blk: &hir::Expr<'_>,
@@ -1018,14 +972,14 @@ impl<'a> State<'a> {
         self.print_else(elseopt)
     }
 
-    pub fn print_array_length(&mut self, len: &hir::ArrayLen) {
+    fn print_array_length(&mut self, len: &hir::ArrayLen) {
         match len {
             hir::ArrayLen::Infer(_, _) => self.word("_"),
             hir::ArrayLen::Body(ct) => self.print_anon_const(ct),
         }
     }
 
-    pub fn print_anon_const(&mut self, constant: &hir::AnonConst) {
+    fn print_anon_const(&mut self, constant: &hir::AnonConst) {
         self.ann.nested(self, Nested::Body(constant.body))
     }
 
@@ -1041,7 +995,7 @@ impl<'a> State<'a> {
 
     /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
     /// `if cond { ... }`.
-    pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
+    fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
         self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
     }
 
@@ -1360,7 +1314,7 @@ impl<'a> State<'a> {
         self.pclose();
     }
 
-    pub fn print_expr(&mut self, expr: &hir::Expr<'_>) {
+    fn print_expr(&mut self, expr: &hir::Expr<'_>) {
         self.maybe_print_comment(expr.span.lo());
         self.print_outer_attributes(self.attrs(expr.hir_id));
         self.ibox(INDENT_UNIT);
@@ -1593,7 +1547,7 @@ impl<'a> State<'a> {
         self.end()
     }
 
-    pub fn print_local_decl(&mut self, loc: &hir::Local<'_>) {
+    fn print_local_decl(&mut self, loc: &hir::Local<'_>) {
         self.print_pat(loc.pat);
         if let Some(ty) = loc.ty {
             self.word_space(":");
@@ -1601,11 +1555,11 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_name(&mut self, name: Symbol) {
+    fn print_name(&mut self, name: Symbol) {
         self.print_ident(Ident::with_dummy_span(name))
     }
 
-    pub fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
+    fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
         self.maybe_print_comment(path.span.lo());
 
         for (i, segment) in path.segments.iter().enumerate() {
@@ -1619,14 +1573,14 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
+    fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
         if segment.ident.name != kw::PathRoot {
             self.print_ident(segment.ident);
             self.print_generic_args(segment.args(), false);
         }
     }
 
-    pub fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) {
+    fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) {
         match *qpath {
             hir::QPath::Resolved(None, path) => self.print_path(path, colons_before_params),
             hir::QPath::Resolved(Some(qself), path) => {
@@ -1743,7 +1697,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
+    fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
         self.print_ident(binding.ident);
         self.print_generic_args(binding.gen_args, false);
         self.space();
@@ -1761,7 +1715,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_pat(&mut self, pat: &hir::Pat<'_>) {
+    fn print_pat(&mut self, pat: &hir::Pat<'_>) {
         self.maybe_print_comment(pat.span.lo());
         self.ann.pre(self, AnnNode::Pat(pat));
         // Pat isn't normalized, but the beauty of it
@@ -1905,7 +1859,7 @@ impl<'a> State<'a> {
         self.ann.post(self, AnnNode::Pat(pat))
     }
 
-    pub fn print_patfield(&mut self, field: &hir::PatField<'_>) {
+    fn print_patfield(&mut self, field: &hir::PatField<'_>) {
         if self.attrs(field.hir_id).is_empty() {
             self.space();
         }
@@ -1919,12 +1873,12 @@ impl<'a> State<'a> {
         self.end();
     }
 
-    pub fn print_param(&mut self, arg: &hir::Param<'_>) {
+    fn print_param(&mut self, arg: &hir::Param<'_>) {
         self.print_outer_attributes(self.attrs(arg.hir_id));
         self.print_pat(arg.pat);
     }
 
-    pub fn print_arm(&mut self, arm: &hir::Arm<'_>) {
+    fn print_arm(&mut self, arm: &hir::Arm<'_>) {
         // I have no idea why this check is necessary, but here it
         // is :(
         if self.attrs(arm.hir_id).is_empty() {
@@ -1976,7 +1930,7 @@ impl<'a> State<'a> {
         self.end() // close enclosing cbox
     }
 
-    pub fn print_fn(
+    fn print_fn(
         &mut self,
         decl: &hir::FnDecl<'_>,
         header: hir::FnHeader,
@@ -2056,14 +2010,14 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
+    fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
         match capture_clause {
             hir::CaptureBy::Value => self.word_space("move"),
             hir::CaptureBy::Ref => {}
         }
     }
 
-    pub fn print_closure_binder(
+    fn print_closure_binder(
         &mut self,
         binder: hir::ClosureBinder,
         generic_params: &[GenericParam<'_>],
@@ -2083,7 +2037,8 @@ impl<'a> State<'a> {
 
         match binder {
             hir::ClosureBinder::Default => {}
-            // we need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional restrictions
+            // We need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional
+            // restrictions.
             hir::ClosureBinder::For { .. } if generic_params.is_empty() => self.word("for<>"),
             hir::ClosureBinder::For { .. } => {
                 self.word("for");
@@ -2099,7 +2054,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_bounds<'b>(
+    fn print_bounds<'b>(
         &mut self,
         prefix: &'static str,
         bounds: impl IntoIterator<Item = &'b hir::GenericBound<'b>>,
@@ -2137,7 +2092,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
+    fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
         if !generic_params.is_empty() {
             self.word("<");
 
@@ -2147,7 +2102,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_generic_param(&mut self, param: &GenericParam<'_>) {
+    fn print_generic_param(&mut self, param: &GenericParam<'_>) {
         if let GenericParamKind::Const { .. } = param.kind {
             self.word_space("const");
         }
@@ -2175,11 +2130,11 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) {
+    fn print_lifetime(&mut self, lifetime: &hir::Lifetime) {
         self.print_ident(lifetime.ident)
     }
 
-    pub fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
+    fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
         if generics.predicates.is_empty() {
             return;
         }
@@ -2236,7 +2191,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) {
+    fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) {
         match mutbl {
             hir::Mutability::Mut => self.word_nbsp("mut"),
             hir::Mutability::Not => {
@@ -2247,12 +2202,12 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) {
+    fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) {
         self.print_mutability(mt.mutbl, print_const);
         self.print_type(mt.ty);
     }
 
-    pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
+    fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
         if let hir::FnRetTy::DefaultReturn(..) = decl.output {
             return;
         }
@@ -2271,7 +2226,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_ty_fn(
+    fn print_ty_fn(
         &mut self,
         abi: Abi,
         unsafety: hir::Unsafety,
@@ -2299,7 +2254,7 @@ impl<'a> State<'a> {
         self.end();
     }
 
-    pub fn print_fn_header_info(&mut self, header: hir::FnHeader) {
+    fn print_fn_header_info(&mut self, header: hir::FnHeader) {
         self.print_constness(header.constness);
 
         match header.asyncness {
@@ -2317,21 +2272,21 @@ impl<'a> State<'a> {
         self.word("fn")
     }
 
-    pub fn print_constness(&mut self, s: hir::Constness) {
+    fn print_constness(&mut self, s: hir::Constness) {
         match s {
             hir::Constness::NotConst => {}
             hir::Constness::Const => self.word_nbsp("const"),
         }
     }
 
-    pub fn print_unsafety(&mut self, s: hir::Unsafety) {
+    fn print_unsafety(&mut self, s: hir::Unsafety) {
         match s {
             hir::Unsafety::Normal => {}
             hir::Unsafety::Unsafe => self.word_nbsp("unsafe"),
         }
     }
 
-    pub fn print_is_auto(&mut self, s: hir::IsAuto) {
+    fn print_is_auto(&mut self, s: hir::IsAuto) {
         match s {
             hir::IsAuto::Yes => self.word_nbsp("auto"),
             hir::IsAuto::No => {}
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index e426b937542..e506c150f7d 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -56,7 +56,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // closure sooner rather than later, so first examine the expected
         // type, and see if can glean a closure kind from there.
         let (expected_sig, expected_kind) = match expected.to_option(self) {
-            Some(ty) => self.deduce_closure_signature(ty),
+            Some(ty) => {
+                self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
+            }
             None => (None, None),
         };
         let body = self.tcx.hir().body(closure.body);
@@ -688,8 +690,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn")
         });
 
+        let closure_span = self.tcx.def_span(expr_def_id);
         let ret_ty = ret_coercion.borrow().expected_ty();
-        let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
+        let ret_ty = self.try_structurally_resolve_type(closure_span, ret_ty);
 
         let get_future_output = |predicate: ty::Predicate<'tcx>, span| {
             // Search for a pending obligation like
@@ -711,8 +714,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         };
 
-        let span = self.tcx.def_span(expr_def_id);
-
         let output_ty = match *ret_ty.kind() {
             ty::Infer(ty::TyVar(ret_vid)) => {
                 self.obligations_for_self_ty(ret_vid).find_map(|obligation| {
@@ -726,17 +727,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?,
             ty::Error(_) => return None,
             _ => span_bug!(
-                span,
+                closure_span,
                 "async fn generator return type not an inference variable: {ret_ty}"
             ),
         };
 
-        let output_ty = self.normalize(span, output_ty);
+        let output_ty = self.normalize(closure_span, output_ty);
 
         // async fn that have opaque types in their return type need to redo the conversion to inference variables
         // as they fetch the still opaque version from the signature.
         let InferOk { value: output_ty, obligations } = self
-            .replace_opaque_types_with_inference_vars(output_ty, body_def_id, span, self.param_env);
+            .replace_opaque_types_with_inference_vars(
+                output_ty,
+                body_def_id,
+                closure_span,
+                self.param_env,
+            );
         self.register_predicates(obligations);
 
         Some(output_ty)
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index c0332a48be3..c762e684480 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -16,6 +16,7 @@ use rustc_errors::{
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
 use rustc_hir::{ExprKind, Node, QPath};
 use rustc_hir_analysis::astconv::AstConv;
 use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
@@ -28,7 +29,7 @@ use rustc_infer::infer::TypeTrace;
 use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::visit::TypeVisitableExt;
-use rustc_middle::ty::{self, IsSuggestable, Ty};
+use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{self, sym, BytePos, Span};
@@ -722,6 +723,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         &mut err,
                         fn_def_id,
                         callee_ty,
+                        call_expr,
+                        None,
                         Some(mismatch_idx),
                         is_method,
                     );
@@ -826,6 +829,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 &mut err,
                 fn_def_id,
                 callee_ty,
+                call_expr,
+                Some(expected_ty),
                 Some(expected_idx.as_usize()),
                 is_method,
             );
@@ -1208,7 +1213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // Call out where the function is defined
-        self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method);
+        self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method);
 
         // And add a suggestion block for all of the parameters
         let suggestion_text = match suggestion_text {
@@ -1899,6 +1904,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err: &mut Diagnostic,
         callable_def_id: Option<DefId>,
         callee_ty: Option<Ty<'tcx>>,
+        call_expr: &'tcx hir::Expr<'tcx>,
+        expected_ty: Option<Ty<'tcx>>,
         // A specific argument should be labeled, instead of all of them
         expected_idx: Option<usize>,
         is_method: bool,
@@ -2015,6 +2022,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let param = expected_idx
                 .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
             let (kind, span) = if let Some(param) = param {
+                // Try to find earlier invocations of this closure to find if the type mismatch
+                // is because of inference. If we find one, point at them.
+                let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] };
+                let node = self.tcx
+                    .opt_local_def_id_to_hir_id(self.tcx.hir().get_parent_item(call_expr.hir_id))
+                    .and_then(|hir_id| self.tcx.hir().find(hir_id));
+                match node {
+                    Some(hir::Node::Item(item)) => call_finder.visit_item(item),
+                    Some(hir::Node::TraitItem(item)) => call_finder.visit_trait_item(item),
+                    Some(hir::Node::ImplItem(item)) => call_finder.visit_impl_item(item),
+                    _ => {}
+                }
+                let typeck = self.typeck_results.borrow();
+                for (rcvr, args) in call_finder.calls {
+                    if rcvr.hir_id.owner == typeck.hir_owner
+                        && let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id)
+                        && let ty::Closure(call_def_id, _) = rcvr_ty.kind()
+                        && def_id == *call_def_id
+                        && let Some(idx) = expected_idx
+                        && let Some(arg) = args.get(idx)
+                        && let Some(arg_ty) = typeck.node_type_opt(arg.hir_id)
+                        && let Some(expected_ty) = expected_ty
+                        && self.can_eq(self.param_env, arg_ty, expected_ty)
+                    {
+                        let mut sp: MultiSpan = vec![arg.span].into();
+                        sp.push_span_label(
+                            arg.span,
+                            format!("expected because this argument is of type `{arg_ty}`"),
+                        );
+                        sp.push_span_label(rcvr.span, "in this closure call");
+                        err.span_note(
+                            sp,
+                            format!(
+                                "expected because the closure was earlier called with an \
+                                argument of type `{arg_ty}`",
+                            ),
+                        );
+                        break;
+                    }
+                }
+
                 ("closure parameter", param.span)
             } else {
                 ("closure", self.tcx.def_span(def_id))
@@ -2028,3 +2076,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 }
+
+struct FindClosureArg<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    calls: Vec<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
+}
+
+impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> {
+    type NestedFilter = rustc_middle::hir::nested_filter::All;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
+
+    fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+        if let hir::ExprKind::Call(rcvr, args) = ex.kind {
+            self.calls.push((rcvr, args));
+        }
+        hir::intravisit::walk_expr(self, ex);
+    }
+}
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 3f9c9b3381b..072c7a9f36a 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1504,9 +1504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         {
             let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand);
             if has_shorthand_field_name {
-                let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
-                    s.print_qpath(qpath, false)
-                });
+                let path = rustc_hir_pretty::qpath_to_string(qpath);
                 let mut err = struct_span_err!(
                     self.tcx.sess,
                     pat.span,
@@ -1688,9 +1686,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return None;
             }
 
-            let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
-                s.print_qpath(qpath, false)
-            });
+            let path = rustc_hir_pretty::qpath_to_string(qpath);
             let mut err = struct_span_err!(
                 self.tcx.sess,
                 pat.span,
@@ -1740,9 +1736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             f
                         }
                     }
-                    Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
-                        s.print_pat(field.pat)
-                    }),
+                    Err(_) => rustc_hir_pretty::pat_to_string(field.pat),
                 }
             })
             .collect::<Vec<String>>()
diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl
index 9fa4e0fb27c..5d885e07192 100644
--- a/compiler/rustc_incremental/messages.ftl
+++ b/compiler/rustc_incremental/messages.ftl
@@ -46,8 +46,6 @@ incremental_delete_partial = failed to delete partly initialized session dir `{$
 
 incremental_delete_workproduct = file-system error deleting outdated file `{$path}`: {$err}
 
-incremental_field_associated_value_expected = associated value expected for `{$name}`
-
 incremental_finalize = error finalizing incremental compilation session directory `{$path}`: {$err}
 
 incremental_finalized_gc_failed =
@@ -63,25 +61,15 @@ incremental_load_dep_graph = could not load dep-graph from `{$path}`: {$err}
 
 incremental_lock_unsupported =
     the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation
-incremental_malformed_cgu_name =
-    found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case).
 
 incremental_missing_depnode = missing `DepNode` variant
 
 incremental_missing_if_this_changed = no `#[rustc_if_this_changed]` annotation detected
 
-incremental_missing_query_depgraph =
-    found CGU-reuse attribute but `-Zquery-dep-graph` was not specified
-
 incremental_move_dep_graph = failed to move dependency graph from `{$from}` to `{$to}`: {$err}
 
 incremental_no_cfg = no cfg attribute
 
-incremental_no_field = no field `{$name}`
-
-incremental_no_module_named =
-    no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names}
-
 incremental_no_path = no path from `{$source}` to `{$target}`
 
 incremental_not_clean = `{$dep_node_str}` should be clean but is not
@@ -107,8 +95,6 @@ incremental_undefined_clean_dirty_assertions_item =
 
 incremental_unknown_item = unknown item `{$name}`
 
-incremental_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
-
 incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name}
 
 incremental_unrecognized_depnode_label = dep-node label `{$label}` not recognized
diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs
index deb87678365..05ed4f7598d 100644
--- a/compiler/rustc_incremental/src/errors.rs
+++ b/compiler/rustc_incremental/src/errors.rs
@@ -41,56 +41,6 @@ pub struct NoPath {
 }
 
 #[derive(Diagnostic)]
-#[diag(incremental_unknown_reuse_kind)]
-pub struct UnknownReuseKind {
-    #[primary_span]
-    pub span: Span,
-    pub kind: Symbol,
-}
-
-#[derive(Diagnostic)]
-#[diag(incremental_missing_query_depgraph)]
-pub struct MissingQueryDepGraph {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(incremental_malformed_cgu_name)]
-pub struct MalformedCguName {
-    #[primary_span]
-    pub span: Span,
-    pub user_path: String,
-    pub crate_name: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(incremental_no_module_named)]
-pub struct NoModuleNamed<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub user_path: &'a str,
-    pub cgu_name: Symbol,
-    pub cgu_names: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(incremental_field_associated_value_expected)]
-pub struct FieldAssociatedValueExpected {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-}
-
-#[derive(Diagnostic)]
-#[diag(incremental_no_field)]
-pub struct NoField {
-    #[primary_span]
-    pub span: Span,
-    pub name: Symbol,
-}
-
-#[derive(Diagnostic)]
 #[diag(incremental_assertion_auto)]
 pub struct AssertionAuto<'a> {
     #[primary_span]
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs
index 220ea194a6d..bdae07a3946 100644
--- a/compiler/rustc_incremental/src/lib.rs
+++ b/compiler/rustc_incremental/src/lib.rs
@@ -2,6 +2,9 @@
 
 #![deny(missing_docs)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(never_type)]
 #![recursion_limit = "256"]
 #![deny(rustc::untranslatable_diagnostic)]
@@ -13,7 +16,6 @@ extern crate rustc_middle;
 extern crate tracing;
 
 mod assert_dep_graph;
-pub mod assert_module_sources;
 mod errors;
 mod persist;
 
diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs
index 665297da20f..5d929394eb0 100644
--- a/compiler/rustc_infer/src/infer/equate.rs
+++ b/compiler/rustc_infer/src/infer/equate.rs
@@ -56,7 +56,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
         // performing trait matching (which then performs equality
         // unification).
 
-        relate::relate_args(self, a_arg, b_arg)
+        relate::relate_args_invariantly(self, a_arg, b_arg)
     }
 
     fn relate_with_variance<T: Relate<'tcx>>(
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 72cfc1337e2..12dcb711820 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -59,20 +59,19 @@ use crate::traits::{
 };
 
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_errors::{error_code, Applicability, DiagnosticBuilder, DiagnosticStyledString};
 use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::Node;
 use rustc_middle::dep_graph::DepContext;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
 use rustc_middle::ty::{
-    self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt,
+    self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
+    TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
 };
 use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
 use rustc_target::spec::abi;
@@ -2317,113 +2316,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        // Attempt to obtain the span of the parameter so we can
-        // suggest adding an explicit lifetime bound to it.
-        let generics = self.tcx.generics_of(generic_param_scope);
-        // type_param_span is (span, has_bounds)
-        let mut is_synthetic = false;
-        let mut ast_generics = None;
-        let type_param_span = match bound_kind {
-            GenericKind::Param(ref param) => {
-                // Account for the case where `param` corresponds to `Self`,
-                // which doesn't have the expected type argument.
-                if !(generics.has_self && param.index == 0) {
-                    let type_param = generics.type_param(param, self.tcx);
-                    is_synthetic = type_param.kind.is_synthetic();
-                    type_param.def_id.as_local().map(|def_id| {
-                        // Get the `hir::Param` to verify whether it already has any bounds.
-                        // We do this to avoid suggesting code that ends up as `T: 'a'b`,
-                        // instead we suggest `T: 'a + 'b` in that case.
-                        let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
-                        ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id);
-                        let bounds =
-                            ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
-                        // `sp` only covers `T`, change it so that it covers
-                        // `T:` when appropriate
-                        if let Some(span) = bounds {
-                            (span, true)
-                        } else {
-                            let sp = self.tcx.def_span(def_id);
-                            (sp.shrink_to_hi(), false)
-                        }
-                    })
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        };
-
-        let new_lt = {
-            let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
-            let lts_names =
-                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
-                    .flat_map(|g| &g.params)
-                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
-                    .map(|p| p.name.as_str())
-                    .collect::<Vec<_>>();
-            possible
-                .find(|candidate| !lts_names.contains(&&candidate[..]))
-                .unwrap_or("'lt".to_string())
-        };
-
-        let mut add_lt_suggs: Vec<Option<_>> = vec![];
-        if is_synthetic {
-            if let Some(ast_generics) = ast_generics {
-                let named_lifetime_param_exist = ast_generics.params.iter().any(|p| {
-                    matches!(
-                        p.kind,
-                        hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
-                    )
-                });
-                if named_lifetime_param_exist && let [param, ..] = ast_generics.params
-                {
-                    add_lt_suggs.push(Some((
-                        self.tcx.def_span(param.def_id).shrink_to_lo(),
-                        format!("{new_lt}, "),
-                    )));
-                } else {
-                    add_lt_suggs
-                        .push(Some((ast_generics.span.shrink_to_hi(), format!("<{new_lt}>"))));
-                }
-            }
-        } else {
-            if let [param, ..] = &generics.params[..] && let Some(def_id) = param.def_id.as_local()
-            {
-                add_lt_suggs
-                    .push(Some((self.tcx.def_span(def_id).shrink_to_lo(), format!("{new_lt}, "))));
-            }
-        }
-
-        if let Some(ast_generics) = ast_generics {
-            for p in ast_generics.params {
-                if p.is_elided_lifetime() {
-                    if self
-                        .tcx
-                        .sess
-                        .source_map()
-                        .span_to_prev_source(p.span.shrink_to_hi())
-                        .ok()
-                        .is_some_and(|s| *s.as_bytes().last().unwrap() == b'&')
-                    {
-                        add_lt_suggs
-                            .push(Some(
-                                (
-                                    p.span.shrink_to_hi(),
-                                    if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span)
-                                        && snip.starts_with(' ')
-                                    {
-                                        new_lt.to_string()
-                                    } else {
-                                        format!("{new_lt} ")
-                                    }
-                                )
-                            ));
-                    } else {
-                        add_lt_suggs.push(Some((p.span.shrink_to_hi(), format!("<{new_lt}>"))));
-                    }
-                }
-            }
+        if let Some(SubregionOrigin::CompareImplItemObligation {
+            span,
+            impl_item_def_id,
+            trait_item_def_id,
+        }) = origin
+        {
+            return self.report_extra_impl_obligation(
+                span,
+                impl_item_def_id,
+                trait_item_def_id,
+                &format!("`{bound_kind}: {sub}`"),
+            );
         }
 
         let labeled_user_string = match bound_kind {
@@ -2437,223 +2341,207 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             },
         };
 
-        if let Some(SubregionOrigin::CompareImplItemObligation {
+        let mut err = self.tcx.sess.struct_span_err_with_code(
             span,
-            impl_item_def_id,
-            trait_item_def_id,
-        }) = origin
-        {
-            return self.report_extra_impl_obligation(
-                span,
-                impl_item_def_id,
-                trait_item_def_id,
-                &format!("`{bound_kind}: {sub}`"),
-            );
+            format!("{labeled_user_string} may not live long enough"),
+            match sub.kind() {
+                ty::ReEarlyBound(_) | ty::ReFree(_) if sub.has_name() => error_code!(E0309),
+                ty::ReStatic => error_code!(E0310),
+                _ => error_code!(E0311),
+            },
+        );
+
+        '_explain: {
+            let (description, span) = match sub.kind() {
+                ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
+                    msg_span_from_named_region(self.tcx, sub, Some(span))
+                }
+                _ => (format!("lifetime `{sub}`"), Some(span)),
+            };
+            let prefix = format!("{labeled_user_string} must be valid for ");
+            label_msg_span(&mut err, &prefix, description, span, "...");
+            if let Some(origin) = origin {
+                self.note_region_origin(&mut err, &origin);
+            }
         }
 
-        fn binding_suggestion<'tcx, S: fmt::Display>(
-            err: &mut Diagnostic,
-            type_param_span: Option<(Span, bool)>,
-            bound_kind: GenericKind<'tcx>,
-            sub: S,
-            add_lt_suggs: Vec<Option<(Span, String)>>,
-        ) {
+        'suggestion: {
             let msg = "consider adding an explicit lifetime bound";
-            if let Some((sp, has_lifetimes)) = type_param_span {
-                let suggestion =
-                    if has_lifetimes { format!(" + {sub}") } else { format!(": {sub}") };
-                let mut suggestions = vec![(sp, suggestion)];
-                for add_lt_sugg in add_lt_suggs.into_iter().flatten() {
-                    suggestions.push(add_lt_sugg);
+
+            if (bound_kind, sub).has_infer_regions()
+                || (bound_kind, sub).has_placeholders()
+                || !bound_kind.is_suggestable(self.tcx, false)
+            {
+                let lt_name = sub.get_name_or_anon().to_string();
+                err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
+                break 'suggestion;
+            }
+
+            let mut generic_param_scope = generic_param_scope;
+            while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
+                generic_param_scope = self.tcx.local_parent(generic_param_scope);
+            }
+
+            // type_param_sugg_span is (span, has_bounds)
+            let (type_scope, type_param_sugg_span) = match bound_kind {
+                GenericKind::Param(ref param) => {
+                    let generics = self.tcx.generics_of(generic_param_scope);
+                    let def_id = generics.type_param(param, self.tcx).def_id.expect_local();
+                    let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
+                    // Get the `hir::Param` to verify whether it already has any bounds.
+                    // We do this to avoid suggesting code that ends up as `T: 'a'b`,
+                    // instead we suggest `T: 'a + 'b` in that case.
+                    let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
+                    let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
+                        Some(span) => Some((span, true)),
+                        // If `param` corresponds to `Self`, no usable suggestion span.
+                        None if generics.has_self && param.index == 0 => None,
+                        None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false)),
+                    };
+                    (scope, sugg_span)
                 }
-                err.multipart_suggestion_verbose(
-                    format!("{msg}..."),
-                    suggestions,
-                    Applicability::MaybeIncorrect, // Issue #41966
-                );
-            } else {
-                let consider = format!("{msg} `{bound_kind}: {sub}`...");
-                err.help(consider);
-            }
-        }
-
-        let new_binding_suggestion =
-            |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| {
-                let msg = "consider introducing an explicit lifetime bound";
-                if let Some((sp, has_lifetimes)) = type_param_span {
-                    let suggestion =
-                        if has_lifetimes { format!(" + {new_lt}") } else { format!(": {new_lt}") };
-                    let mut sugg =
-                        vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {new_lt}"))];
-                    for lt in add_lt_suggs.clone().into_iter().flatten() {
-                        sugg.push(lt);
-                        sugg.rotate_right(1);
-                    }
-                    // `MaybeIncorrect` due to issue #41966.
-                    err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect);
+                _ => (generic_param_scope, None),
+            };
+            let suggestion_scope = {
+                let lifetime_scope = match sub.kind() {
+                    ty::ReStatic => hir::def_id::CRATE_DEF_ID,
+                    _ => match self.tcx.is_suitable_region(sub) {
+                        Some(info) => info.def_id,
+                        None => generic_param_scope,
+                    },
+                };
+                match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
+                    true => type_scope,
+                    false => lifetime_scope,
                 }
             };
 
-        #[derive(Debug)]
-        enum SubOrigin<'hir> {
-            GAT(&'hir hir::Generics<'hir>),
-            Impl,
-            Trait,
-            Fn,
-            Unknown,
-        }
-        let sub_origin = 'origin: {
-            match *sub {
-                ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => {
-                    let node = self.tcx.hir().get_if_local(def_id).unwrap();
-                    match node {
-                        Node::GenericParam(param) => {
-                            for h in self.tcx.hir().parent_iter(param.hir_id) {
-                                break 'origin match h.1 {
-                                    Node::ImplItem(hir::ImplItem {
-                                        kind: hir::ImplItemKind::Type(..),
-                                        generics,
-                                        ..
-                                    })
-                                    | Node::TraitItem(hir::TraitItem {
-                                        kind: hir::TraitItemKind::Type(..),
-                                        generics,
-                                        ..
-                                    }) => SubOrigin::GAT(generics),
-                                    Node::ImplItem(hir::ImplItem {
-                                        kind: hir::ImplItemKind::Fn(..),
-                                        ..
-                                    })
-                                    | Node::TraitItem(hir::TraitItem {
-                                        kind: hir::TraitItemKind::Fn(..),
-                                        ..
-                                    })
-                                    | Node::Item(hir::Item {
-                                        kind: hir::ItemKind::Fn(..), ..
-                                    }) => SubOrigin::Fn,
-                                    Node::Item(hir::Item {
-                                        kind: hir::ItemKind::Trait(..),
-                                        ..
-                                    }) => SubOrigin::Trait,
-                                    Node::Item(hir::Item {
-                                        kind: hir::ItemKind::Impl(..), ..
-                                    }) => SubOrigin::Impl,
-                                    _ => continue,
-                                };
-                            }
+            let mut suggs = vec![];
+            let lt_name = self.suggest_name_region(sub, &mut suggs);
+
+            if let Some((sp, has_lifetimes)) = type_param_sugg_span
+                && suggestion_scope == type_scope
+            {
+                let suggestion =
+                    if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
+                suggs.push((sp, suggestion))
+            } else {
+                let generics = self.tcx.hir().get_generics(suggestion_scope).unwrap();
+                let pred = format!("{bound_kind}: {lt_name}");
+                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
+                suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
+            }
+
+            err.multipart_suggestion_verbose(
+                format!("{msg}"),
+                suggs,
+                Applicability::MaybeIncorrect, // Issue #41966
+            );
+        }
+
+        err
+    }
+
+    pub fn suggest_name_region(
+        &self,
+        lifetime: Region<'tcx>,
+        add_lt_suggs: &mut Vec<(Span, String)>,
+    ) -> String {
+        struct LifetimeReplaceVisitor<'tcx, 'a> {
+            tcx: TyCtxt<'tcx>,
+            needle: hir::LifetimeName,
+            new_lt: &'a str,
+            add_lt_suggs: &'a mut Vec<(Span, String)>,
+        }
+
+        impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
+            fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
+                if lt.res == self.needle {
+                    let (pos, span) = lt.suggestion_position();
+                    let new_lt = &self.new_lt;
+                    let sugg = match pos {
+                        hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
+                        hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
+                        hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
+                        hir::LifetimeSuggestionPosition::ElidedPathArgument => {
+                            format!("{new_lt}, ")
                         }
-                        _ => {}
+                        hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
+                    };
+                    self.add_lt_suggs.push((span, sugg));
+                }
+            }
+
+            fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
+                let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
+                    return hir::intravisit::walk_ty(self, ty);
+                };
+                let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
+                if let Some(&(_, b)) =
+                    opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
+                {
+                    let prev_needle =
+                        std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
+                    for bound in opaque_ty.bounds {
+                        self.visit_param_bound(bound);
                     }
+                    self.needle = prev_needle;
                 }
-                _ => {}
             }
-            SubOrigin::Unknown
-        };
-        debug!(?sub_origin);
-
-        let mut err = match (*sub, sub_origin) {
-            // In the case of GATs, we have to be careful. If we a type parameter `T` on an impl,
-            // but a lifetime `'a` on an associated type, then we might need to suggest adding
-            // `where T: 'a`. Importantly, this is on the GAT span, not on the `T` declaration.
-            (ty::ReEarlyBound(ty::EarlyBoundRegion { name: _, .. }), SubOrigin::GAT(generics)) => {
-                // Does the required lifetime have a nice name we can print?
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
-                    span,
-                    E0309,
-                    "{} may not live long enough",
-                    labeled_user_string
-                );
-                let pred = format!("{bound_kind}: {sub}");
-                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
-                err.span_suggestion(
-                    generics.tail_span_for_predicate_suggestion(),
-                    "consider adding a where clause",
-                    suggestion,
-                    Applicability::MaybeIncorrect,
-                );
-                err
-            }
-            (
-                ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. })
-                | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }),
-                _,
-            ) if name != kw::UnderscoreLifetime => {
-                // Does the required lifetime have a nice name we can print?
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
-                    span,
-                    E0309,
-                    "{} may not live long enough",
-                    labeled_user_string
-                );
-                // Explicitly use the name instead of `sub`'s `Display` impl. The `Display` impl
-                // for the bound is not suitable for suggestions when `-Zverbose` is set because it
-                // uses `Debug` output, so we handle it specially here so that suggestions are
-                // always correct.
-                binding_suggestion(&mut err, type_param_span, bound_kind, name, vec![]);
-                err
-            }
-
-            (ty::ReStatic, _) => {
-                // Does the required lifetime have a nice name we can print?
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
-                    span,
-                    E0310,
-                    "{} may not live long enough",
-                    labeled_user_string
-                );
-                binding_suggestion(&mut err, type_param_span, bound_kind, "'static", vec![]);
-                err
+        }
+
+        let (lifetime_def_id, lifetime_scope) = match self.tcx.is_suitable_region(lifetime) {
+            Some(info) if !lifetime.has_name() => {
+                (info.boundregion.get_id().unwrap().expect_local(), info.def_id)
             }
+            _ => return lifetime.get_name_or_anon().to_string(),
+        };
 
-            _ => {
-                // If not, be less specific.
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
-                    span,
-                    E0311,
-                    "{} may not live long enough",
-                    labeled_user_string
-                );
-                note_and_explain_region(
-                    self.tcx,
-                    &mut err,
-                    &format!("{labeled_user_string} must be valid for "),
-                    sub,
-                    "...",
-                    None,
-                );
-                if let Some(infer::RelateParamBound(_, t, _)) = origin {
-                    let t = self.resolve_vars_if_possible(t);
-                    match t.kind() {
-                        // We've got:
-                        // fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
-                        // suggest:
-                        // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
-                        ty::Closure(..) | ty::Alias(ty::Opaque, ..) => {
-                            new_binding_suggestion(&mut err, type_param_span);
-                        }
-                        _ => {
-                            binding_suggestion(
-                                &mut err,
-                                type_param_span,
-                                bound_kind,
-                                new_lt,
-                                add_lt_suggs,
-                            );
-                        }
+        let new_lt = {
+            let generics = self.tcx.generics_of(lifetime_scope);
+            let mut used_names =
+                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
+                    .flat_map(|g| &g.params)
+                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
+                    .map(|p| p.name)
+                    .collect::<Vec<_>>();
+            if let Some(hir_id) = self.tcx.opt_local_def_id_to_hir_id(lifetime_scope) {
+                // consider late-bound lifetimes ...
+                used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(|p| {
+                    match p {
+                        ty::BoundVariableKind::Region(lt) => lt.get_name(),
+                        _ => None,
                     }
-                }
-                err
+                }))
             }
+            (b'a'..=b'z')
+                .map(|c| format!("'{}", c as char))
+                .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
+                .unwrap_or("'lt".to_string())
         };
 
-        if let Some(origin) = origin {
-            self.note_region_origin(&mut err, &origin);
-        }
-        err
+        let mut visitor = LifetimeReplaceVisitor {
+            tcx: self.tcx,
+            needle: hir::LifetimeName::Param(lifetime_def_id),
+            add_lt_suggs,
+            new_lt: &new_lt,
+        };
+        match self.tcx.hir().expect_owner(lifetime_scope) {
+            hir::OwnerNode::Item(i) => visitor.visit_item(i),
+            hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
+            hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
+            hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
+            hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
+        }
+
+        let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
+        let sugg = ast_generics
+            .span_for_lifetime_suggestion()
+            .map(|span| (span, format!("{new_lt}, ")))
+            .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
+        add_lt_suggs.push(sugg);
+
+        new_lt
     }
 
     fn report_sub_sup_conflict(
diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs
index dd7f8d35441..c1e65ffe0a6 100644
--- a/compiler/rustc_infer/src/infer/generalize.rs
+++ b/compiler/rustc_infer/src/infer/generalize.rs
@@ -183,7 +183,7 @@ where
             // Avoid fetching the variance if we are in an invariant
             // context; no need, and it can induce dependency cycles
             // (e.g., #41849).
-            relate::relate_args(self, a_subst, b_subst)
+            relate::relate_args_invariantly(self, a_subst, b_subst)
         } else {
             let tcx = self.tcx();
             let opt_variances = tcx.variances_of(item_def_id);
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index e92ba05aa67..4a6d1bc682b 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -13,6 +13,9 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(associated_type_bounds)]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 0e8f93cef17..718dbaaafcc 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -957,10 +957,9 @@ pub fn start_codegen<'tcx>(
         codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
     });
 
-    // Don't run these test assertions when not doing codegen. Compiletest tries to build
+    // Don't run this test assertions when not doing codegen. Compiletest tries to build
     // build-fail tests in check mode first and expects it to not give an error in that case.
     if tcx.sess.opts.output_types.should_codegen() {
-        rustc_incremental::assert_module_sources::assert_module_sources(tcx);
         rustc_symbol_mangling::test::report_symbol_names(tcx);
     }
 
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 2510ce71460..7799af37008 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -9,6 +9,7 @@ use rustc_session::config::DebugInfo;
 use rustc_session::config::Input;
 use rustc_session::config::InstrumentXRay;
 use rustc_session::config::LinkSelfContained;
+use rustc_session::config::Polonius;
 use rustc_session::config::TraitSolver;
 use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
 use rustc_session::config::{
@@ -814,7 +815,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(panic_abort_tests, true);
     tracked!(panic_in_drop, PanicStrategy::Abort);
     tracked!(plt, Some(true));
-    tracked!(polonius, true);
+    tracked!(polonius, Polonius::Legacy);
     tracked!(precise_enum_drop_elaboration, false);
     tracked!(print_fuel, Some("abc".to_string()));
     tracked!(profile, true);
diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs
index ff4c81e2fc9..53c5f4cd26e 100644
--- a/compiler/rustc_lint/src/async_fn_in_trait.rs
+++ b/compiler/rustc_lint/src/async_fn_in_trait.rs
@@ -11,7 +11,6 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust
-    /// # #![feature(async_fn_in_trait)]
     /// pub trait Trait {
     ///     async fn method(&self);
     /// }
@@ -33,7 +32,6 @@ declare_lint! {
     /// For example, this code is invalid:
     ///
     /// ```rust,compile_fail
-    /// # #![feature(async_fn_in_trait)]
     /// pub trait Trait {
     ///     async fn method(&self) {}
     /// }
@@ -51,7 +49,6 @@ declare_lint! {
     /// For example, instead of:
     ///
     /// ```rust
-    /// # #![feature(async_fn_in_trait)]
     /// pub trait Trait {
     ///     async fn method(&self) {}
     /// }
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 536f78a73ed..be9465ba23d 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2258,23 +2258,19 @@ impl EarlyLintPass for IncompleteInternalFeatures {
             .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
             .filter(|(&name, _)| features.incomplete(name) || features.internal(name))
             .for_each(|(&name, &span)| {
-                let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
-                    .map(|n| BuiltinFeatureIssueNote { n });
-
                 if features.incomplete(name) {
+                    let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
+                        .map(|n| BuiltinFeatureIssueNote { n });
                     let help =
                         HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp);
+
                     cx.emit_spanned_lint(
                         INCOMPLETE_FEATURES,
                         span,
                         BuiltinIncompleteFeatures { name, note, help },
                     );
                 } else {
-                    cx.emit_spanned_lint(
-                        INTERNAL_FEATURES,
-                        span,
-                        BuiltinInternalFeatures { name, note },
-                    );
+                    cx.emit_spanned_lint(INTERNAL_FEATURES, span, BuiltinInternalFeatures { name });
                 }
             });
     }
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 3c5cde4309b..1ed9b86d66c 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -727,11 +727,14 @@ pub trait LintContext: Sized {
                                 .collect::<Vec<_>>();
                             possibilities.sort();
 
+                            let mut should_print_possibilities = true;
                             if let Some((value, value_span)) = value {
                                 if best_match_values.contains(&Some(value)) {
                                     db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect);
+                                    should_print_possibilities = false;
                                 } else if best_match_values.contains(&None) {
                                     db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect);
+                                    should_print_possibilities = false;
                                 } else if let Some(first_value) = possibilities.first() {
                                     db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect);
                                 } else {
@@ -741,13 +744,25 @@ pub trait LintContext: Sized {
                                 db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
                             }
 
-                            if !possibilities.is_empty() {
+                            if !possibilities.is_empty() && should_print_possibilities {
                                 let possibilities = possibilities.join("`, `");
                                 db.help(format!("expected values for `{best_match}` are: `{possibilities}`"));
                             }
                         } else {
                             db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
                         }
+                    } else if !possibilities.is_empty() {
+                        let mut possibilities = possibilities.iter()
+                            .map(Symbol::as_str)
+                            .collect::<Vec<_>>();
+                        possibilities.sort();
+                        let possibilities = possibilities.join("`, `");
+
+                        // The list of expected names can be long (even by default) and
+                        // so the diagnostic produced can take a lot of space. To avoid
+                        // cloging the user output we only want to print that diagnostic
+                        // once.
+                        db.help_once(format!("expected names are: `{possibilities}`"));
                     }
                 },
                 BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index af2132fb899..b9e455e6c2a 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -27,6 +27,8 @@
 
 #![allow(rustc::potential_query_instability)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(array_windows)]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
@@ -504,6 +506,11 @@ fn register_builtins(store: &mut LintStore) {
         "replaced with another group of lints, see RFC \
          <https://rust-lang.github.io/rfcs/2145-type-privacy.html> for more information",
     );
+    store.register_removed(
+        "invalid_alignment",
+        "converted into hard error, see PR #104616 \
+         <https://github.com/rust-lang/rust/pull/104616> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index c776d3bb7fe..594ef97b3ff 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -412,8 +412,6 @@ pub struct BuiltinIncompleteFeatures {
 #[note]
 pub struct BuiltinInternalFeatures {
     pub name: Symbol,
-    #[subdiagnostic]
-    pub note: Option<BuiltinFeatureIssueNote>,
 }
 
 #[derive(Subdiagnostic)]
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 44cf1591c7d..d252f651c78 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -140,13 +140,15 @@ declare_lint! {
 pub struct TypeLimits {
     /// Id of the last visited negated expression
     negated_expr_id: Option<hir::HirId>,
+    /// Span of the last visited negated expression
+    negated_expr_span: Option<Span>,
 }
 
 impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]);
 
 impl TypeLimits {
     pub fn new() -> TypeLimits {
-        TypeLimits { negated_expr_id: None }
+        TypeLimits { negated_expr_id: None, negated_expr_span: None }
     }
 }
 
@@ -426,17 +428,15 @@ fn lint_int_literal<'tcx>(
             return;
         }
 
-        let lit = cx
-            .sess()
-            .source_map()
-            .span_to_snippet(lit.span)
-            .expect("must get snippet from literal");
+        let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span };
+        let lit =
+            cx.sess().source_map().span_to_snippet(span).expect("must get snippet from literal");
         let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
             .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
 
         cx.emit_spanned_lint(
             OVERFLOWING_LITERALS,
-            e.span,
+            span,
             OverflowingInt { ty: t.name_str(), lit, min, max, help },
         );
     }
@@ -622,9 +622,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
         match e.kind {
             hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => {
-                // propagate negation, if the negation itself isn't negated
+                // Propagate negation, if the negation itself isn't negated
                 if self.negated_expr_id != Some(e.hir_id) {
                     self.negated_expr_id = Some(expr.hir_id);
+                    self.negated_expr_span = Some(e.span);
                 }
             }
             hir::ExprKind::Binary(binop, ref l, ref r) => {
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 69b462d32bd..96a98393fb2 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -987,45 +987,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `invalid_alignment` lint detects dereferences of misaligned pointers during
-    /// constant evaluation.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,compile_fail
-    /// #![feature(const_mut_refs)]
-    /// const FOO: () = unsafe {
-    ///     let x = &[0_u8; 4];
-    ///     let y = x.as_ptr().cast::<u32>();
-    ///     let mut z = 123;
-    ///     y.copy_to_nonoverlapping(&mut z, 1); // the address of a `u8` array is unknown
-    ///     // and thus we don't know if it is aligned enough for copying a `u32`.
-    /// };
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// The compiler allowed dereferencing raw pointers irrespective of alignment
-    /// during const eval due to the const evaluator at the time not making it easy
-    /// or cheap to check. Now that it is both, this is not accepted anymore.
-    ///
-    /// Since it was undefined behaviour to begin with, this breakage does not violate
-    /// Rust's stability guarantees. Using undefined behaviour can cause arbitrary
-    /// behaviour, including failure to build.
-    ///
-    /// [future-incompatible]: ../index.md#future-incompatible-lints
-    pub INVALID_ALIGNMENT,
-    Deny,
-    "raw pointers must be aligned before dereferencing",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
-        reference: "issue #68585 <https://github.com/rust-lang/rust/issues/104616>",
-    };
-}
-
-declare_lint! {
     /// The `exported_private_dependencies` lint detects private dependencies
     /// that are exposed in a public interface.
     ///
@@ -3430,7 +3391,6 @@ declare_lint_pass! {
         INDIRECT_STRUCTURAL_MATCH,
         INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
         INLINE_NO_SANITIZE,
-        INVALID_ALIGNMENT,
         INVALID_DOC_ATTRIBUTES,
         INVALID_MACRO_EXPORT_ARGUMENTS,
         INVALID_TYPE_PARAM_DEFAULT,
@@ -4574,7 +4534,6 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust,compile_fail
-    /// #![feature(return_position_impl_trait_in_trait)]
     /// #![deny(refining_impl_trait)]
     ///
     /// use std::fmt::Display;
diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs
index eb70961503d..518c20c9fa8 100644
--- a/compiler/rustc_llvm/src/lib.rs
+++ b/compiler/rustc_llvm/src/lib.rs
@@ -1,6 +1,9 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 
 // NOTE: This crate only exists to allow linking on mingw targets.
 
diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs
index 3e43bcb3b8f..0c9ec556549 100644
--- a/compiler/rustc_log/src/lib.rs
+++ b/compiler/rustc_log/src/lib.rs
@@ -74,6 +74,11 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> {
         Some(v) => &v != "0",
     };
 
+    let verbose_thread_ids = match env::var_os(String::from(env) + "_THREAD_IDS") {
+        None => false,
+        Some(v) => &v == "1",
+    };
+
     let layer = tracing_tree::HierarchicalLayer::default()
         .with_writer(io::stderr)
         .with_indent_lines(true)
@@ -81,9 +86,9 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> {
         .with_targets(true)
         .with_verbose_exit(verbose_entry_exit)
         .with_verbose_entry(verbose_entry_exit)
-        .with_indent_amount(2);
-    #[cfg(all(parallel_compiler, debug_assertions))]
-    let layer = layer.with_thread_ids(true).with_thread_names(true);
+        .with_indent_amount(2)
+        .with_thread_ids(verbose_thread_ids)
+        .with_thread_names(verbose_thread_ids);
 
     let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
     match env::var(format!("{env}_BACKTRACE")) {
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index fa77b36c4c5..ddeb39669dc 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -1,4 +1,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(decl_macro)]
 #![feature(extract_if)]
 #![feature(generators)]
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 4af2d83e9c7..77643715fff 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -970,12 +970,15 @@ impl<'hir> Map<'hir> {
                 // SyntaxContext of the visibility.
                 sig.span.find_ancestor_in_same_ctxt(*outer_span).unwrap_or(*outer_span)
             }
+            // Impls, including their where clauses.
+            Node::Item(Item {
+                kind: ItemKind::Impl(Impl { generics, .. }),
+                span: outer_span,
+                ..
+            }) => until_within(*outer_span, generics.where_clause_span),
             // Constants and Statics.
             Node::Item(Item {
-                kind:
-                    ItemKind::Const(ty, ..)
-                    | ItemKind::Static(ty, ..)
-                    | ItemKind::Impl(Impl { self_ty: ty, .. }),
+                kind: ItemKind::Const(ty, ..) | ItemKind::Static(ty, ..),
                 span: outer_span,
                 ..
             })
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index fe4fc3761b3..dee18dc1162 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -23,6 +23,8 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(allocator_api)]
 #![feature(array_windows)]
 #![feature(assert_matches)]
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 89934e4350e..08f7434a4a9 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -581,13 +581,13 @@ pub enum BindingMode {
     ByRef(BorrowKind),
 }
 
-#[derive(Clone, Debug, HashStable)]
+#[derive(Clone, Debug, HashStable, TypeVisitable)]
 pub struct FieldPat<'tcx> {
     pub field: FieldIdx,
     pub pattern: Box<Pat<'tcx>>,
 }
 
-#[derive(Clone, Debug, HashStable)]
+#[derive(Clone, Debug, HashStable, TypeVisitable)]
 pub struct Pat<'tcx> {
     pub ty: Ty<'tcx>,
     pub span: Span,
@@ -664,7 +664,7 @@ impl<'tcx> IntoDiagnosticArg for Pat<'tcx> {
     }
 }
 
-#[derive(Clone, Debug, HashStable)]
+#[derive(Clone, Debug, HashStable, TypeVisitable)]
 pub struct Ascription<'tcx> {
     pub annotation: CanonicalUserTypeAnnotation<'tcx>,
     /// Variance to use when relating the `user_ty` to the **type of the value being
@@ -688,7 +688,7 @@ pub struct Ascription<'tcx> {
     pub variance: ty::Variance,
 }
 
-#[derive(Clone, Debug, HashStable)]
+#[derive(Clone, Debug, HashStable, TypeVisitable)]
 pub enum PatKind<'tcx> {
     /// A wildcard pattern: `_`.
     Wild,
@@ -702,7 +702,9 @@ pub enum PatKind<'tcx> {
     Binding {
         mutability: Mutability,
         name: Symbol,
+        #[type_visitable(ignore)]
         mode: BindingMode,
+        #[type_visitable(ignore)]
         var: LocalVarId,
         ty: Ty<'tcx>,
         subpattern: Option<Box<Pat<'tcx>>>,
@@ -771,10 +773,11 @@ pub enum PatKind<'tcx> {
     },
 }
 
-#[derive(Clone, Debug, PartialEq, HashStable)]
+#[derive(Clone, Debug, PartialEq, HashStable, TypeVisitable)]
 pub struct PatRange<'tcx> {
     pub lo: mir::Const<'tcx>,
     pub hi: mir::Const<'tcx>,
+    #[type_visitable(ignore)]
     pub end: RangeEnd,
 }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 83adbc3c790..1ccb81dcb9b 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1057,16 +1057,21 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Returns the `DefId` and the `BoundRegionKind` corresponding to the given region.
-    pub fn is_suitable_region(self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
-        let (suitable_region_binding_scope, bound_region) = match *region {
-            ty::ReFree(ref free_region) => {
-                (free_region.scope.expect_local(), free_region.bound_region)
+    pub fn is_suitable_region(self, mut region: Region<'tcx>) -> Option<FreeRegionInfo> {
+        let (suitable_region_binding_scope, bound_region) = loop {
+            let def_id = match region.kind() {
+                ty::ReFree(fr) => fr.bound_region.get_id()?.as_local()?,
+                ty::ReEarlyBound(ebr) => ebr.def_id.expect_local(),
+                _ => return None, // not a free region
+            };
+            let scope = self.local_parent(def_id);
+            if self.def_kind(scope) == DefKind::OpaqueTy {
+                // Lifetime params of opaque types are synthetic and thus irrelevant to
+                // diagnostics. Map them back to their origin!
+                region = self.map_rpit_lifetime_to_fn_lifetime(def_id);
+                continue;
             }
-            ty::ReEarlyBound(ref ebr) => (
-                self.local_parent(ebr.def_id.expect_local()),
-                ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
-            ),
-            _ => return None, // not a free region
+            break (scope, ty::BrNamed(def_id.into(), self.item_name(def_id.into())));
         };
 
         let is_impl_item = match self.hir().find_by_def_id(suitable_region_binding_scope) {
@@ -1074,7 +1079,7 @@ impl<'tcx> TyCtxt<'tcx> {
             Some(Node::ImplItem(..)) => {
                 self.is_bound_region_in_impl_item(suitable_region_binding_scope)
             }
-            _ => return None,
+            _ => false,
         };
 
         Some(FreeRegionInfo {
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index e9d763afa68..96268006353 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -8,6 +8,7 @@ use crate::ty::error::{ExpectedFound, TypeError};
 use crate::ty::{self, Expr, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable};
 use crate::ty::{GenericArg, GenericArgKind, GenericArgsRef};
 use rustc_hir as hir;
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_target::spec::abi;
 use std::iter;
@@ -134,7 +135,7 @@ pub fn relate_type_and_mut<'tcx, R: TypeRelation<'tcx>>(
 }
 
 #[inline]
-pub fn relate_args<'tcx, R: TypeRelation<'tcx>>(
+pub fn relate_args_invariantly<'tcx, R: TypeRelation<'tcx>>(
     relation: &mut R,
     a_arg: GenericArgsRef<'tcx>,
     b_arg: GenericArgsRef<'tcx>,
@@ -273,7 +274,20 @@ impl<'tcx> Relate<'tcx> for ty::AliasTy<'tcx> {
         if a.def_id != b.def_id {
             Err(TypeError::ProjectionMismatched(expected_found(relation, a.def_id, b.def_id)))
         } else {
-            let args = relation.relate(a.args, b.args)?;
+            let args = match relation.tcx().def_kind(a.def_id) {
+                DefKind::OpaqueTy => relate_args_with_variances(
+                    relation,
+                    a.def_id,
+                    relation.tcx().variances_of(a.def_id),
+                    a.args,
+                    b.args,
+                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
+                )?,
+                DefKind::AssocTy | DefKind::AssocConst | DefKind::TyAlias => {
+                    relate_args_invariantly(relation, a.args, b.args)?
+                }
+                def => bug!("unknown alias DefKind: {def:?}"),
+            };
             Ok(relation.tcx().mk_alias_ty(a.def_id, args))
         }
     }
@@ -315,7 +329,7 @@ impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> {
         if a.def_id != b.def_id {
             Err(TypeError::Traits(expected_found(relation, a.def_id, b.def_id)))
         } else {
-            let args = relate_args(relation, a.args, b.args)?;
+            let args = relate_args_invariantly(relation, a.args, b.args)?;
             Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args))
         }
     }
@@ -331,7 +345,7 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> {
         if a.def_id != b.def_id {
             Err(TypeError::Traits(expected_found(relation, a.def_id, b.def_id)))
         } else {
-            let args = relate_args(relation, a.args, b.args)?;
+            let args = relate_args_invariantly(relation, a.args, b.args)?;
             Ok(ty::ExistentialTraitRef { def_id: a.def_id, args })
         }
     }
@@ -449,7 +463,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             // All Generator types with the same id represent
             // the (anonymous) type of the same generator expression. So
             // all of their regions should be equated.
-            let args = relation.relate(a_args, b_args)?;
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
             Ok(Ty::new_generator(tcx, a_id, args, movability))
         }
 
@@ -459,7 +473,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             // All GeneratorWitness types with the same id represent
             // the (anonymous) type of the same generator expression. So
             // all of their regions should be equated.
-            let args = relation.relate(a_args, b_args)?;
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
             Ok(Ty::new_generator_witness(tcx, a_id, args))
         }
 
@@ -467,7 +481,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             // All Closure types with the same id represent
             // the (anonymous) type of the same closure expression. So
             // all of their regions should be equated.
-            let args = relation.relate(a_args, b_args)?;
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
             Ok(Ty::new_closure(tcx, a_id, &args))
         }
 
@@ -536,24 +550,6 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             Ok(Ty::new_fn_ptr(tcx, fty))
         }
 
-        // The args of opaque types may not all be invariant, so we have
-        // to treat them separately from other aliases.
-        (
-            &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, args: a_args, .. }),
-            &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, args: b_args, .. }),
-        ) if a_def_id == b_def_id => {
-            let opt_variances = tcx.variances_of(a_def_id);
-            let args = relate_args_with_variances(
-                relation,
-                a_def_id,
-                opt_variances,
-                a_args,
-                b_args,
-                false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
-            )?;
-            Ok(Ty::new_opaque(tcx, a_def_id, args))
-        }
-
         // Alias tend to mostly already be handled downstream due to normalization.
         (&ty::Alias(a_kind, a_data), &ty::Alias(b_kind, b_data)) => {
             let alias_ty = relation.relate(a_data, b_data)?;
@@ -709,7 +705,7 @@ impl<'tcx> Relate<'tcx> for ty::ClosureArgs<'tcx> {
         a: ty::ClosureArgs<'tcx>,
         b: ty::ClosureArgs<'tcx>,
     ) -> RelateResult<'tcx, ty::ClosureArgs<'tcx>> {
-        let args = relate_args(relation, a.args, b.args)?;
+        let args = relate_args_invariantly(relation, a.args, b.args)?;
         Ok(ty::ClosureArgs { args })
     }
 }
@@ -720,7 +716,7 @@ impl<'tcx> Relate<'tcx> for ty::GeneratorArgs<'tcx> {
         a: ty::GeneratorArgs<'tcx>,
         b: ty::GeneratorArgs<'tcx>,
     ) -> RelateResult<'tcx, ty::GeneratorArgs<'tcx>> {
-        let args = relate_args(relation, a.args, b.args)?;
+        let args = relate_args_invariantly(relation, a.args, b.args)?;
         Ok(ty::GeneratorArgs { args })
     }
 }
@@ -731,7 +727,7 @@ impl<'tcx> Relate<'tcx> for GenericArgsRef<'tcx> {
         a: GenericArgsRef<'tcx>,
         b: GenericArgsRef<'tcx>,
     ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-        relate_args(relation, a, b)
+        relate_args_invariantly(relation, a, b)
     }
 }
 
@@ -835,19 +831,6 @@ impl<'tcx> Relate<'tcx> for Term<'tcx> {
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::ProjectionPredicate<'tcx>,
-        b: ty::ProjectionPredicate<'tcx>,
-    ) -> RelateResult<'tcx, ty::ProjectionPredicate<'tcx>> {
-        Ok(ty::ProjectionPredicate {
-            projection_ty: relation.relate(a.projection_ty, b.projection_ty)?,
-            term: relation.relate(a.term, b.term)?,
-        })
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // Error handling
 
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index ce021923f64..db3886b32be 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -173,7 +173,7 @@ mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count ->
 
 mir_build_literal_in_range_out_of_bounds =
     literal out of range for `{$ty}`
-    .label = this value doesn't fit in `{$ty}` whose maximum value is `{$max}`
+    .label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`
 
 mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
     lower range bound must be less than or equal to upper
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index bee5ac550dd..4f98932a88d 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -551,6 +551,7 @@ pub struct LiteralOutOfRange<'tcx> {
     #[label]
     pub span: Span,
     pub ty: Ty<'tcx>,
+    pub min: i128,
     pub max: u128,
 }
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index d440ca31926..93434dd3cc2 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -19,7 +19,7 @@ use rustc_hir::HirId;
 use rustc_middle::thir::visit::{self, Visitor};
 use rustc_middle::thir::*;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::lint::builtin::{
     BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
 };
@@ -682,6 +682,12 @@ fn non_exhaustive_match<'p, 'tcx>(
     arms: &[ArmId],
     expr_span: Span,
 ) -> ErrorGuaranteed {
+    for &arm in arms {
+        if let Err(err) = thir[arm].pattern.error_reported() {
+            return err;
+        }
+    }
+
     let is_empty_match = arms.is_empty();
     let non_empty_enum = match scrut_ty.kind() {
         ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index ae442466029..fde6defd87f 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -7,7 +7,7 @@ use rustc_middle::mir;
 use rustc_middle::thir::{FieldPat, Pat, PatKind};
 use rustc_middle::ty::{self, Ty, TyCtxt, ValTree};
 use rustc_session::lint;
-use rustc_span::Span;
+use rustc_span::{ErrorGuaranteed, Span};
 use rustc_target::abi::{FieldIdx, VariantIdx};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{self, ObligationCause};
@@ -48,7 +48,7 @@ struct ConstToPat<'tcx> {
     // This tracks if we emitted some hard error for a given const value, so that
     // we will not subsequently issue an irrelevant lint for the same const
     // value.
-    saw_const_match_error: Cell<bool>,
+    saw_const_match_error: Cell<Option<ErrorGuaranteed>>,
 
     // This tracks if we emitted some diagnostic for a given const value, so that
     // we will not subsequently issue an irrelevant lint for the same const
@@ -84,7 +84,7 @@ impl<'tcx> ConstToPat<'tcx> {
             span,
             infcx,
             param_env: pat_ctxt.param_env,
-            saw_const_match_error: Cell::new(false),
+            saw_const_match_error: Cell::new(None),
             saw_const_match_lint: Cell::new(false),
             behind_reference: Cell::new(false),
             treat_byte_string_as_slice: pat_ctxt
@@ -154,7 +154,7 @@ impl<'tcx> ConstToPat<'tcx> {
             }),
         };
 
-        if !self.saw_const_match_error.get() {
+        if self.saw_const_match_error.get().is_none() {
             // If we were able to successfully convert the const to some pat (possibly with some
             // lints, but no errors), double-check that all types in the const implement
             // `Structural` and `PartialEq`.
@@ -180,23 +180,26 @@ impl<'tcx> ConstToPat<'tcx> {
 
             if let Some(non_sm_ty) = structural {
                 if !self.type_has_partial_eq_impl(cv.ty()) {
-                    if let ty::Adt(def, ..) = non_sm_ty.kind() {
+                    let e = if let ty::Adt(def, ..) = non_sm_ty.kind() {
                         if def.is_union() {
                             let err = UnionPattern { span: self.span };
-                            self.tcx().sess.emit_err(err);
+                            self.tcx().sess.emit_err(err)
                         } else {
                             // fatal avoids ICE from resolution of nonexistent method (rare case).
                             self.tcx()
                                 .sess
-                                .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty });
+                                .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty })
                         }
                     } else {
                         let err = InvalidPattern { span: self.span, non_sm_ty };
-                        self.tcx().sess.emit_err(err);
-                    }
+                        self.tcx().sess.emit_err(err)
+                    };
                     // All branches above emitted an error. Don't print any more lints.
-                    // The pattern we return is irrelevant since we errored.
-                    return Box::new(Pat { span: self.span, ty: cv.ty(), kind: PatKind::Wild });
+                    // We errored. Signal that in the pattern, so that follow up errors can be silenced.
+                    let kind = PatKind::Constant {
+                        value: mir::Const::Ty(ty::Const::new_error(self.tcx(), e, cv.ty())),
+                    };
+                    return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
                 } else if !self.saw_const_match_lint.get() {
                     if let Some(mir_structural_match_violation) = mir_structural_match_violation {
                         match non_sm_ty.kind() {
@@ -330,7 +333,7 @@ impl<'tcx> ConstToPat<'tcx> {
             // Backwards compatibility hack because we can't cause hard errors on these
             // types, so we compare them via `PartialEq::eq` at runtime.
             ty::Adt(..) if !self.type_marked_structural(ty) && self.behind_reference.get() => {
-                if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
+                if self.saw_const_match_error.get().is_none() && !self.saw_const_match_lint.get() {
                     self.saw_const_match_lint.set(true);
                     tcx.emit_spanned_lint(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
@@ -345,18 +348,18 @@ impl<'tcx> ConstToPat<'tcx> {
                 return Err(FallbackToOpaqueConst);
             }
             ty::FnDef(..) => {
-                self.saw_const_match_error.set(true);
-                tcx.sess.emit_err(InvalidPattern { span, non_sm_ty: ty });
-                // We errored, so the pattern we generate is irrelevant.
-                PatKind::Wild
+                let e = tcx.sess.emit_err(InvalidPattern { span, non_sm_ty: ty });
+                self.saw_const_match_error.set(Some(e));
+                // We errored. Signal that in the pattern, so that follow up errors can be silenced.
+                PatKind::Constant { value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)) }
             }
             ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
                 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,);
-                self.saw_const_match_error.set(true);
                 let err = TypeNotStructural { span, non_sm_ty: ty };
-                tcx.sess.emit_err(err);
-                // We errored, so the pattern we generate is irrelevant.
-                PatKind::Wild
+                let e = tcx.sess.emit_err(err);
+                self.saw_const_match_error.set(Some(e));
+                // We errored. Signal that in the pattern, so that follow up errors can be silenced.
+                PatKind::Constant { value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)) }
             }
             ty::Adt(adt_def, args) if adt_def.is_enum() => {
                 let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
@@ -416,7 +419,9 @@ impl<'tcx> ConstToPat<'tcx> {
                 // instead of a hard error.
                 ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
                     if self.behind_reference.get() {
-                        if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
+                        if self.saw_const_match_error.get().is_none()
+                            && !self.saw_const_match_lint.get()
+                        {
                             self.saw_const_match_lint.set(true);
                             tcx.emit_spanned_lint(
                                 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
@@ -427,14 +432,20 @@ impl<'tcx> ConstToPat<'tcx> {
                         }
                         return Err(FallbackToOpaqueConst);
                     } else {
-                        if !self.saw_const_match_error.get() {
-                            self.saw_const_match_error.set(true);
+                        if let Some(e) = self.saw_const_match_error.get() {
+                            // We already errored. Signal that in the pattern, so that follow up errors can be silenced.
+                            PatKind::Constant {
+                                value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)),
+                            }
+                        } else {
                             let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
-                            tcx.sess.emit_err(err);
+                            let e = tcx.sess.emit_err(err);
+                            self.saw_const_match_error.set(Some(e));
+                            // We errored. Signal that in the pattern, so that follow up errors can be silenced.
+                            PatKind::Constant {
+                                value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)),
+                            }
                         }
-                        tcx.sess.delay_span_bug(span, "`saw_const_match_error` set but no error?");
-                        // We errored, so the pattern we generate is irrelevant.
-                        PatKind::Wild
                     }
                 }
                 // All other references are converted into deref patterns and then recursively
@@ -443,11 +454,11 @@ impl<'tcx> ConstToPat<'tcx> {
                 _ => {
                     if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
                         let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
-                        tcx.sess.emit_err(err);
-
-                        // FIXME: introduce PatKind::Error to silence follow up diagnostics due to unreachable patterns.
-                        // We errored, so the pattern we generate is irrelevant.
-                        PatKind::Wild
+                        let e = tcx.sess.emit_err(err);
+                        // We errored. Signal that in the pattern, so that follow up errors can be silenced.
+                        PatKind::Constant {
+                            value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)),
+                        }
                     } else {
                         let old = self.behind_reference.replace(true);
                         // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
@@ -474,15 +485,15 @@ impl<'tcx> ConstToPat<'tcx> {
             }
             ty::FnPtr(..) | ty::RawPtr(..) => unreachable!(),
             _ => {
-                self.saw_const_match_error.set(true);
                 let err = InvalidPattern { span, non_sm_ty: ty };
-                tcx.sess.emit_err(err);
-                // We errored, so the pattern we generate is irrelevant.
-                PatKind::Wild
+                let e = tcx.sess.emit_err(err);
+                self.saw_const_match_error.set(Some(e));
+                // We errored. Signal that in the pattern, so that follow up errors can be silenced.
+                PatKind::Constant { value: mir::Const::Ty(ty::Const::new_error(tcx, e, ty)) }
             }
         };
 
-        if !self.saw_const_match_error.get()
+        if self.saw_const_match_error.get().is_none()
             && !self.saw_const_match_lint.get()
             && mir_structural_match_violation
             // FIXME(#73448): Find a way to bring const qualification into parity with
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index 3ee4befa121..a7a000ba31c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -39,8 +39,8 @@
 //!
 //! Splitting is implemented in the [`Constructor::split`] function. We don't do splitting for
 //! or-patterns; instead we just try the alternatives one-by-one. For details on splitting
-//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]; for slices, see
-//! [`SplitVarLenSlice`].
+//! wildcards, see [`Constructor::split`]; for integer ranges, see
+//! [`IntRange::split`]; for slices, see [`Slice::split`].
 
 use std::cell::Cell;
 use std::cmp::{self, max, min, Ordering};
@@ -52,6 +52,7 @@ use smallvec::{smallvec, SmallVec};
 
 use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
 use rustc_data_structures::captures::Captures;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{HirId, RangeEnd};
 use rustc_index::Idx;
 use rustc_middle::middle::stability::EvalResult;
@@ -86,6 +87,13 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
     pats
 }
 
+/// Whether we have seen a constructor in the column or not.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum Presence {
+    Unseen,
+    Seen,
+}
+
 /// An inclusive interval, used for precise integer exhaustiveness checking.
 /// `IntRange`s always store a contiguous range. This means that values are
 /// encoded such that `0` encodes the minimum value for the integer,
@@ -186,7 +194,105 @@ impl IntRange {
         (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton()
     }
 
-    /// Only used for displaying the range properly.
+    /// Partition a range of integers into disjoint subranges. This does constructor splitting for
+    /// integer ranges as explained at the top of the file.
+    ///
+    /// This returns an output that covers `self`. The output is split so that the only
+    /// intersections between an output range and a column range are inclusions. No output range
+    /// straddles the boundary of one of the inputs.
+    ///
+    /// Additionally, we track for each output range whether it is covered by one of the column ranges or not.
+    ///
+    /// The following input:
+    /// ```text
+    ///   (--------------------------) // `self`
+    /// (------) (----------)    (-)
+    ///     (------) (--------)
+    /// ```
+    /// is first intersected with `self`:
+    /// ```text
+    ///   (--------------------------) // `self`
+    ///   (----) (----------)    (-)
+    ///     (------) (--------)
+    /// ```
+    /// and then iterated over as follows:
+    /// ```text
+    ///   (-(--)-(-)-(------)-)--(-)-
+    /// ```
+    /// where each sequence of dashes is an output range, and dashes outside parentheses are marked
+    /// as `Presence::Missing`.
+    fn split(
+        &self,
+        column_ranges: impl Iterator<Item = IntRange>,
+    ) -> impl Iterator<Item = (Presence, IntRange)> {
+        /// Represents a boundary between 2 integers. Because the intervals spanning boundaries must be
+        /// able to cover every integer, we need to be able to represent 2^128 + 1 such boundaries.
+        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+        enum IntBoundary {
+            JustBefore(u128),
+            AfterMax,
+        }
+
+        fn unpack_intrange(range: IntRange) -> [IntBoundary; 2] {
+            use IntBoundary::*;
+            let (lo, hi) = range.boundaries();
+            let lo = JustBefore(lo);
+            let hi = match hi.checked_add(1) {
+                Some(m) => JustBefore(m),
+                None => AfterMax,
+            };
+            [lo, hi]
+        }
+
+        // The boundaries of ranges in `column_ranges` intersected with `self`.
+        // We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
+        // a range and -1 if it ends it. When the count is > 0 between two boundaries, we
+        // are within an input range.
+        let mut boundaries: Vec<(IntBoundary, isize)> = column_ranges
+            .filter_map(|r| self.intersection(&r))
+            .map(unpack_intrange)
+            .flat_map(|[lo, hi]| [(lo, 1), (hi, -1)])
+            .collect();
+        // We sort by boundary, and for each boundary we sort the "closing parentheses" first. The
+        // order of +1/-1 for a same boundary value is actually irrelevant, because we only look at
+        // the accumulated count between distinct boundary values.
+        boundaries.sort_unstable();
+
+        let [self_start, self_end] = unpack_intrange(self.clone());
+        // Accumulate parenthesis counts.
+        let mut paren_counter = 0isize;
+        // Gather pairs of adjacent boundaries.
+        let mut prev_bdy = self_start;
+        boundaries
+            .into_iter()
+            // End with the end of the range. The count is ignored.
+            .chain(once((self_end, 0)))
+            // List pairs of adjacent boundaries and the count between them.
+            .map(move |(bdy, delta)| {
+                // `delta` affects the count as we cross `bdy`, so the relevant count between
+                // `prev_bdy` and `bdy` is untouched by `delta`.
+                let ret = (prev_bdy, paren_counter, bdy);
+                prev_bdy = bdy;
+                paren_counter += delta;
+                ret
+            })
+            // Skip empty ranges.
+            .filter(|&(prev_bdy, _, bdy)| prev_bdy != bdy)
+            // Convert back to ranges.
+            .map(move |(prev_bdy, paren_count, bdy)| {
+                use IntBoundary::*;
+                use Presence::*;
+                let presence = if paren_count > 0 { Seen } else { Unseen };
+                let range = match (prev_bdy, bdy) {
+                    (JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
+                    (JustBefore(n), AfterMax) => n..=u128::MAX,
+                    _ => unreachable!(), // Ruled out by the sorting and filtering we did
+                };
+                (presence, IntRange { range })
+            })
+    }
+
+    /// Only used for displaying the range.
     fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
         let (lo, hi) = self.boundaries();
 
@@ -254,18 +360,6 @@ impl IntRange {
             );
         }
     }
-
-    /// See `Constructor::is_covered_by`
-    fn is_covered_by(&self, other: &Self) -> bool {
-        if self.intersection(other).is_some() {
-            // Constructor splitting should ensure that all intersections we encounter are actually
-            // inclusions.
-            assert!(self.is_subrange(other));
-            true
-        } else {
-            false
-        }
-    }
 }
 
 /// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and
@@ -279,101 +373,6 @@ impl fmt::Debug for IntRange {
     }
 }
 
-/// Represents a border between 2 integers. Because the intervals spanning borders must be able to
-/// cover every integer, we need to be able to represent 2^128 + 1 such borders.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-enum IntBorder {
-    JustBefore(u128),
-    AfterMax,
-}
-
-/// A range of integers that is partitioned into disjoint subranges. This does constructor
-/// splitting for integer ranges as explained at the top of the file.
-///
-/// This is fed multiple ranges, and returns an output that covers the input, but is split so that
-/// the only intersections between an output range and a seen range are inclusions. No output range
-/// straddles the boundary of one of the inputs.
-///
-/// The following input:
-/// ```text
-///   |-------------------------| // `self`
-/// |------|  |----------|   |----|
-///    |-------| |-------|
-/// ```
-/// would be iterated over as follows:
-/// ```text
-///   ||---|--||-|---|---|---|--|
-/// ```
-#[derive(Debug, Clone)]
-struct SplitIntRange {
-    /// The range we are splitting
-    range: IntRange,
-    /// The borders of ranges we have seen. They are all contained within `range`. This is kept
-    /// sorted.
-    borders: Vec<IntBorder>,
-}
-
-impl SplitIntRange {
-    fn new(range: IntRange) -> Self {
-        SplitIntRange { range, borders: Vec::new() }
-    }
-
-    /// Internal use
-    fn to_borders(r: IntRange) -> [IntBorder; 2] {
-        use IntBorder::*;
-        let (lo, hi) = r.boundaries();
-        let lo = JustBefore(lo);
-        let hi = match hi.checked_add(1) {
-            Some(m) => JustBefore(m),
-            None => AfterMax,
-        };
-        [lo, hi]
-    }
-
-    /// Add ranges relative to which we split.
-    fn split(&mut self, ranges: impl Iterator<Item = IntRange>) {
-        let this_range = &self.range;
-        let included_ranges = ranges.filter_map(|r| this_range.intersection(&r));
-        let included_borders = included_ranges.flat_map(|r| {
-            let borders = Self::to_borders(r);
-            once(borders[0]).chain(once(borders[1]))
-        });
-        self.borders.extend(included_borders);
-        self.borders.sort_unstable();
-    }
-
-    /// Iterate over the contained ranges.
-    fn iter(&self) -> impl Iterator<Item = IntRange> + Captures<'_> {
-        use IntBorder::*;
-
-        let self_range = Self::to_borders(self.range.clone());
-        // Start with the start of the range.
-        let mut prev_border = self_range[0];
-        self.borders
-            .iter()
-            .copied()
-            // End with the end of the range.
-            .chain(once(self_range[1]))
-            // List pairs of adjacent borders.
-            .map(move |border| {
-                let ret = (prev_border, border);
-                prev_border = border;
-                ret
-            })
-            // Skip duplicates.
-            .filter(|(prev_border, border)| prev_border != border)
-            // Finally, convert to ranges.
-            .map(move |(prev_border, border)| {
-                let range = match (prev_border, border) {
-                    (JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
-                    (JustBefore(n), AfterMax) => n..=u128::MAX,
-                    _ => unreachable!(), // Ruled out by the sorting and filtering we did
-                };
-                IntRange { range }
-            })
-    }
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum SliceKind {
     /// Patterns of length `n` (`[x, y]`).
@@ -430,142 +429,164 @@ impl Slice {
     fn is_covered_by(self, other: Self) -> bool {
         other.kind.covers_length(self.arity())
     }
-}
 
-/// This computes constructor splitting for variable-length slices, as explained at the top of the
-/// file.
-///
-/// A slice pattern `[x, .., y]` behaves like the infinite or-pattern `[x, y] | [x, _, y] | [x, _,
-/// _, y] | ...`. The corresponding value constructors are fixed-length array constructors above a
-/// given minimum length. We obviously can't list this infinitude of constructors. Thankfully,
-/// it turns out that for each finite set of slice patterns, all sufficiently large array lengths
-/// are equivalent.
-///
-/// Let's look at an example, where we are trying to split the last pattern:
-/// ```
-/// # fn foo(x: &[bool]) {
-/// match x {
-///     [true, true, ..] => {}
-///     [.., false, false] => {}
-///     [..] => {}
-/// }
-/// # }
-/// ```
-/// Here are the results of specialization for the first few lengths:
-/// ```
-/// # fn foo(x: &[bool]) { match x {
-/// // length 0
-/// [] => {}
-/// // length 1
-/// [_] => {}
-/// // length 2
-/// [true, true] => {}
-/// [false, false] => {}
-/// [_, _] => {}
-/// // length 3
-/// [true, true,  _    ] => {}
-/// [_,    false, false] => {}
-/// [_,    _,     _    ] => {}
-/// // length 4
-/// [true, true, _,     _    ] => {}
-/// [_,    _,    false, false] => {}
-/// [_,    _,    _,     _    ] => {}
-/// // length 5
-/// [true, true, _, _,     _    ] => {}
-/// [_,    _,    _, false, false] => {}
-/// [_,    _,    _, _,     _    ] => {}
-/// # _ => {}
-/// # }}
-/// ```
-///
-/// If we went above length 5, we would simply be inserting more columns full of wildcards in the
-/// middle. This means that the set of witnesses for length `l >= 5` if equivalent to the set for
-/// any other `l' >= 5`: simply add or remove wildcards in the middle to convert between them.
-///
-/// This applies to any set of slice patterns: there will be a length `L` above which all lengths
-/// behave the same. This is exactly what we need for constructor splitting. Therefore a
-/// variable-length slice can be split into a variable-length slice of minimal length `L`, and many
-/// fixed-length slices of lengths `< L`.
-///
-/// For each variable-length pattern `p` with a prefix of length `plₚ` and suffix of length `slₚ`,
-/// only the first `plₚ` and the last `slₚ` elements are examined. Therefore, as long as `L` is
-/// positive (to avoid concerns about empty types), all elements after the maximum prefix length
-/// and before the maximum suffix length are not examined by any variable-length pattern, and
-/// therefore can be added/removed without affecting them - creating equivalent patterns from any
-/// sufficiently-large length.
-///
-/// Of course, if fixed-length patterns exist, we must be sure that our length is large enough to
-/// miss them all, so we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))`
-///
-/// `max_slice` below will be made to have arity `L`.
-#[derive(Debug)]
-struct SplitVarLenSlice {
-    /// If the type is an array, this is its size.
-    array_len: Option<usize>,
-    /// The arity of the input slice.
-    arity: usize,
-    /// The smallest slice bigger than any slice seen. `max_slice.arity()` is the length `L`
-    /// described above.
-    max_slice: SliceKind,
-}
-
-impl SplitVarLenSlice {
-    fn new(prefix: usize, suffix: usize, array_len: Option<usize>) -> Self {
-        SplitVarLenSlice { array_len, arity: prefix + suffix, max_slice: VarLen(prefix, suffix) }
-    }
-
-    /// Pass a set of slices relative to which to split this one.
-    fn split(&mut self, slices: impl Iterator<Item = SliceKind>) {
-        let VarLen(max_prefix_len, max_suffix_len) = &mut self.max_slice else {
-            // No need to split
-            return;
-        };
-        // We grow `self.max_slice` to be larger than all slices encountered, as described above.
-        // For diagnostics, we keep the prefix and suffix lengths separate, but grow them so that
-        // `L = max_prefix_len + max_suffix_len`.
-        let mut max_fixed_len = 0;
-        for slice in slices {
-            match slice {
-                FixedLen(len) => {
-                    max_fixed_len = cmp::max(max_fixed_len, len);
+    /// This computes constructor splitting for variable-length slices, as explained at the top of
+    /// the file.
+    ///
+    /// A slice pattern `[x, .., y]` behaves like the infinite or-pattern `[x, y] | [x, _, y] | [x,
+    /// _, _, y] | etc`. The corresponding value constructors are fixed-length array constructors of
+    /// corresponding lengths. We obviously can't list this infinitude of constructors.
+    /// Thankfully, it turns out that for each finite set of slice patterns, all sufficiently large
+    /// array lengths are equivalent.
+    ///
+    /// Let's look at an example, where we are trying to split the last pattern:
+    /// ```
+    /// # fn foo(x: &[bool]) {
+    /// match x {
+    ///     [true, true, ..] => {}
+    ///     [.., false, false] => {}
+    ///     [..] => {}
+    /// }
+    /// # }
+    /// ```
+    /// Here are the results of specialization for the first few lengths:
+    /// ```
+    /// # fn foo(x: &[bool]) { match x {
+    /// // length 0
+    /// [] => {}
+    /// // length 1
+    /// [_] => {}
+    /// // length 2
+    /// [true, true] => {}
+    /// [false, false] => {}
+    /// [_, _] => {}
+    /// // length 3
+    /// [true, true,  _    ] => {}
+    /// [_,    false, false] => {}
+    /// [_,    _,     _    ] => {}
+    /// // length 4
+    /// [true, true, _,     _    ] => {}
+    /// [_,    _,    false, false] => {}
+    /// [_,    _,    _,     _    ] => {}
+    /// // length 5
+    /// [true, true, _, _,     _    ] => {}
+    /// [_,    _,    _, false, false] => {}
+    /// [_,    _,    _, _,     _    ] => {}
+    /// # _ => {}
+    /// # }}
+    /// ```
+    ///
+    /// We see that above length 4, we are simply inserting columns full of wildcards in the middle.
+    /// This means that specialization and witness computation with slices of length `l >= 4` will
+    /// give equivalent results regardless of `l`. This applies to any set of slice patterns: there
+    /// will be a length `L` above which all lengths behave the same. This is exactly what we need
+    /// for constructor splitting.
+    ///
+    /// A variable-length slice pattern covers all lengths from its arity up to infinity. As we just
+    /// saw, we can split this in two: lengths below `L` are treated individually with a
+    /// fixed-length slice each; lengths above `L` are grouped into a single variable-length slice
+    /// constructor.
+    ///
+    /// For each variable-length slice pattern `p` with a prefix of length `plₚ` and suffix of
+    /// length `slₚ`, only the first `plₚ` and the last `slₚ` elements are examined. Therefore, as
+    /// long as `L` is positive (to avoid concerns about empty types), all elements after the
+    /// maximum prefix length and before the maximum suffix length are not examined by any
+    /// variable-length pattern, and therefore can be ignored. This gives us a way to compute `L`.
+    ///
+    /// Additionally, if fixed-length patterns exist, we must pick an `L` large enough to miss them,
+    /// so we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))`.
+    /// `max_slice` below will be made to have this arity `L`.
+    ///
+    /// If `self` is fixed-length, it is returned as-is.
+    ///
+    /// Additionally, we track for each output slice whether it is covered by one of the column slices or not.
+    fn split(
+        self,
+        column_slices: impl Iterator<Item = Slice>,
+    ) -> impl Iterator<Item = (Presence, Slice)> {
+        // Range of lengths below `L`.
+        let smaller_lengths;
+        let arity = self.arity();
+        let mut max_slice = self.kind;
+        // Tracks the smallest variable-length slice we've seen. Any slice arity above it is
+        // therefore `Presence::Seen` in the column.
+        let mut min_var_len = usize::MAX;
+        // Tracks the fixed-length slices we've seen, to mark them as `Presence::Seen`.
+        let mut seen_fixed_lens = FxHashSet::default();
+        match &mut max_slice {
+            VarLen(max_prefix_len, max_suffix_len) => {
+                // We grow `max_slice` to be larger than all slices encountered, as described above.
+                // For diagnostics, we keep the prefix and suffix lengths separate, but grow them so that
+                // `L = max_prefix_len + max_suffix_len`.
+                let mut max_fixed_len = 0;
+                for slice in column_slices {
+                    match slice.kind {
+                        FixedLen(len) => {
+                            max_fixed_len = cmp::max(max_fixed_len, len);
+                            if arity <= len {
+                                seen_fixed_lens.insert(len);
+                            }
+                        }
+                        VarLen(prefix, suffix) => {
+                            *max_prefix_len = cmp::max(*max_prefix_len, prefix);
+                            *max_suffix_len = cmp::max(*max_suffix_len, suffix);
+                            min_var_len = cmp::min(min_var_len, prefix + suffix);
+                        }
+                    }
                 }
-                VarLen(prefix, suffix) => {
-                    *max_prefix_len = cmp::max(*max_prefix_len, prefix);
-                    *max_suffix_len = cmp::max(*max_suffix_len, suffix);
+                // We want `L = max(L, max_fixed_len + 1)`, modulo the fact that we keep prefix and
+                // suffix separate.
+                if max_fixed_len + 1 >= *max_prefix_len + *max_suffix_len {
+                    // The subtraction can't overflow thanks to the above check.
+                    // The new `max_prefix_len` is larger than its previous value.
+                    *max_prefix_len = max_fixed_len + 1 - *max_suffix_len;
                 }
-            }
-        }
-        // We want `L = max(L, max_fixed_len + 1)`, modulo the fact that we keep prefix and
-        // suffix separate.
-        if max_fixed_len + 1 >= *max_prefix_len + *max_suffix_len {
-            // The subtraction can't overflow thanks to the above check.
-            // The new `max_prefix_len` is larger than its previous value.
-            *max_prefix_len = max_fixed_len + 1 - *max_suffix_len;
-        }
 
-        // We cap the arity of `max_slice` at the array size.
-        match self.array_len {
-            Some(len) if self.max_slice.arity() >= len => self.max_slice = FixedLen(len),
-            _ => {}
-        }
-    }
+                // We cap the arity of `max_slice` at the array size.
+                match self.array_len {
+                    Some(len) if max_slice.arity() >= len => max_slice = FixedLen(len),
+                    _ => {}
+                }
 
-    /// Iterate over the partition of this slice.
-    fn iter(&self) -> impl Iterator<Item = Slice> + Captures<'_> {
-        let smaller_lengths = match self.array_len {
-            // The only admissible fixed-length slice is one of the array size. Whether `max_slice`
-            // is fixed-length or variable-length, it will be the only relevant slice to output
-            // here.
-            Some(_) => 0..0, // empty range
-            // We cover all arities in the range `(self.arity..infinity)`. We split that range into
-            // two: lengths smaller than `max_slice.arity()` are treated independently as
-            // fixed-lengths slices, and lengths above are captured by `max_slice`.
-            None => self.arity..self.max_slice.arity(),
+                smaller_lengths = match self.array_len {
+                    // The only admissible fixed-length slice is one of the array size. Whether `max_slice`
+                    // is fixed-length or variable-length, it will be the only relevant slice to output
+                    // here.
+                    Some(_) => 0..0, // empty range
+                    // We need to cover all arities in the range `(arity..infinity)`. We split that
+                    // range into two: lengths smaller than `max_slice.arity()` are treated
+                    // independently as fixed-lengths slices, and lengths above are captured by
+                    // `max_slice`.
+                    None => self.arity()..max_slice.arity(),
+                };
+            }
+            FixedLen(_) => {
+                // No need to split here. We only track presence.
+                for slice in column_slices {
+                    match slice.kind {
+                        FixedLen(len) => {
+                            if len == arity {
+                                seen_fixed_lens.insert(len);
+                            }
+                        }
+                        VarLen(prefix, suffix) => {
+                            min_var_len = cmp::min(min_var_len, prefix + suffix);
+                        }
+                    }
+                }
+                smaller_lengths = 0..0;
+            }
         };
-        smaller_lengths
-            .map(FixedLen)
-            .chain(once(self.max_slice))
-            .map(move |kind| Slice::new(self.array_len, kind))
+
+        smaller_lengths.map(FixedLen).chain(once(max_slice)).map(move |kind| {
+            let arity = kind.arity();
+            let seen = if min_var_len <= arity || seen_fixed_lens.contains(&arity) {
+                Presence::Seen
+            } else {
+                Presence::Unseen
+            };
+            (seen, Slice::new(self.array_len, kind))
+        })
     }
 }
 
@@ -596,19 +617,23 @@ pub(super) enum Constructor<'tcx> {
     /// boxes for the purposes of exhaustiveness: we must not inspect them, and they
     /// don't count towards making a match exhaustive.
     Opaque,
+    /// Or-pattern.
+    Or,
+    /// Wildcard pattern.
+    Wildcard,
     /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
     /// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
     NonExhaustive,
-    /// Stands for constructors that are not seen in the matrix, as explained in the documentation
-    /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns`
-    /// lint.
+    /// Fake extra constructor for variants that should not be mentioned in diagnostics.
+    /// We use this for variants behind an unstable gate as well as
+    /// `#[doc(hidden)]` ones.
+    Hidden,
+    /// Fake extra constructor for constructors that are not seen in the matrix, as explained in the
+    /// code for [`Constructor::split`]. The carried `bool` is used for the
+    /// `non_exhaustive_omitted_patterns` lint.
     Missing {
-        nonexhaustive_enum_missing_real_variants: bool,
+        nonexhaustive_enum_missing_visible_variants: bool,
     },
-    /// Wildcard pattern.
-    Wildcard,
-    /// Or-pattern.
-    Or,
 }
 
 impl<'tcx> Constructor<'tcx> {
@@ -620,13 +645,18 @@ impl<'tcx> Constructor<'tcx> {
         matches!(self, NonExhaustive)
     }
 
+    pub(super) fn as_variant(&self) -> Option<VariantIdx> {
+        match self {
+            Variant(i) => Some(*i),
+            _ => None,
+        }
+    }
     fn as_int_range(&self) -> Option<&IntRange> {
         match self {
             IntRange(range) => Some(range),
             _ => None,
         }
     }
-
     fn as_slice(&self) -> Option<Slice> {
         match self {
             Slice(slice) => Some(*slice),
@@ -634,32 +664,6 @@ impl<'tcx> Constructor<'tcx> {
         }
     }
 
-    /// Checks if the `Constructor` is a variant and `TyCtxt::eval_stability` returns
-    /// `EvalResult::Deny { .. }`.
-    ///
-    /// This means that the variant has a stdlib unstable feature marking it.
-    pub(super) fn is_unstable_variant(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
-        if let Constructor::Variant(idx) = self && let ty::Adt(adt, _) = pcx.ty.kind() {
-            let variant_def_id = adt.variant(*idx).def_id;
-            // Filter variants that depend on a disabled unstable feature.
-            return matches!(
-                pcx.cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
-                EvalResult::Deny { .. }
-            );
-        }
-        false
-    }
-
-    /// Checks if the `Constructor` is a `Constructor::Variant` with a `#[doc(hidden)]`
-    /// attribute from a type not local to the current crate.
-    pub(super) fn is_doc_hidden_variant(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
-        if let Constructor::Variant(idx) = self && let ty::Adt(adt, _) = pcx.ty.kind() {
-            let variant_def_id = adt.variants()[*idx].def_id;
-            return pcx.cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
-        }
-        false
-    }
-
     fn variant_index_for_adt(&self, adt: ty::AdtDef<'tcx>) -> VariantIdx {
         match *self {
             Variant(idx) => idx,
@@ -695,27 +699,28 @@ impl<'tcx> Constructor<'tcx> {
             | F32Range(..)
             | F64Range(..)
             | IntRange(..)
-            | NonExhaustive
             | Opaque
+            | NonExhaustive
+            | Hidden
             | Missing { .. }
             | Wildcard => 0,
             Or => bug!("The `Or` constructor doesn't have a fixed arity"),
         }
     }
 
-    /// Some constructors (namely `Wildcard`, `IntRange` and `Slice`) actually stand for a set of actual
-    /// constructors (like variants, integers or fixed-sized slices). When specializing for these
-    /// constructors, we want to be specialising for the actual underlying constructors.
+    /// Some constructors (namely `Wildcard`, `IntRange` and `Slice`) actually stand for a set of
+    /// actual constructors (like variants, integers or fixed-sized slices). When specializing for
+    /// these constructors, we want to be specialising for the actual underlying constructors.
     /// Naively, we would simply return the list of constructors they correspond to. We instead are
-    /// more clever: if there are constructors that we know will behave the same wrt the current
-    /// matrix, we keep them grouped. For example, all slices of a sufficiently large length
-    /// will either be all useful or all non-useful with a given matrix.
+    /// more clever: if there are constructors that we know will behave the same w.r.t. the current
+    /// matrix, we keep them grouped. For example, all slices of a sufficiently large length will
+    /// either be all useful or all non-useful with a given matrix.
     ///
     /// See the branches for details on how the splitting is done.
     ///
-    /// This function may discard some irrelevant constructors if this preserves behavior and
-    /// diagnostics. Eg. for the `_` case, we ignore the constructors already present in the
-    /// matrix, unless all of them are.
+    /// This function may discard some irrelevant constructors if this preserves behavior. Eg. for
+    /// the `_` case, we ignore the constructors already present in the column, unless all of them
+    /// are.
     pub(super) fn split<'a>(
         &self,
         pcx: &PatCtxt<'_, '_, 'tcx>,
@@ -726,23 +731,74 @@ impl<'tcx> Constructor<'tcx> {
     {
         match self {
             Wildcard => {
-                let mut split_wildcard = SplitWildcard::new(pcx);
-                split_wildcard.split(pcx, ctors);
-                split_wildcard.into_ctors(pcx)
+                let split_set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, ctors);
+                if !split_set.missing.is_empty() {
+                    // We are splitting a wildcard in order to compute its usefulness. Some constructors are
+                    // not present in the column. The first thing we note is that specializing with any of
+                    // the missing constructors would select exactly the rows with wildcards. Moreover, they
+                    // would all return equivalent results. We can therefore group them all into a
+                    // fictitious `Missing` constructor.
+                    //
+                    // As an important optimization, this function will skip all the present constructors.
+                    // This is correct because specializing with any of the present constructors would
+                    // select a strict superset of the wildcard rows, and thus would only find witnesses
+                    // already found with the `Missing` constructor.
+                    // This does mean that diagnostics are incomplete: in
+                    // ```
+                    // match x {
+                    //   Some(true) => {}
+                    // }
+                    // ```
+                    // we report `None` as missing but not `Some(false)`.
+                    //
+                    // When all the constructors are missing we can equivalently return the `Wildcard`
+                    // constructor on its own. The difference between `Wildcard` and `Missing` will then
+                    // only be in diagnostics.
+
+                    // If some constructors are missing, we typically want to report those constructors,
+                    // e.g.:
+                    // ```
+                    //     enum Direction { N, S, E, W }
+                    //     let Direction::N = ...;
+                    // ```
+                    // we can report 3 witnesses: `S`, `E`, and `W`.
+                    //
+                    // However, if the user didn't actually specify a constructor
+                    // in this arm, e.g., in
+                    // ```
+                    //     let x: (Direction, Direction, bool) = ...;
+                    //     let (_, _, false) = x;
+                    // ```
+                    // we don't want to show all 16 possible witnesses `(<direction-1>, <direction-2>,
+                    // true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we
+                    // prefer to report just a wildcard `_`.
+                    //
+                    // The exception is: if we are at the top-level, for example in an empty match, we
+                    // usually prefer to report the full list of constructors.
+                    let all_missing = split_set.present.is_empty();
+                    let report_when_all_missing =
+                        pcx.is_top_level && !IntRange::is_integral(pcx.ty);
+                    let ctor = if all_missing && !report_when_all_missing {
+                        Wildcard
+                    } else {
+                        Missing {
+                            nonexhaustive_enum_missing_visible_variants: split_set
+                                .nonexhaustive_enum_missing_visible_variants,
+                        }
+                    };
+                    smallvec![ctor]
+                } else {
+                    split_set.present
+                }
             }
-            // Fast-track if the range is trivial. In particular, we don't do the overlapping
-            // ranges check.
-            IntRange(ctor_range) if !ctor_range.is_singleton() => {
-                let mut split_range = SplitIntRange::new(ctor_range.clone());
-                let int_ranges = ctors.filter_map(|ctor| ctor.as_int_range());
-                split_range.split(int_ranges.cloned());
-                split_range.iter().map(IntRange).collect()
+            // Fast-track if the range is trivial.
+            IntRange(this_range) if !this_range.is_singleton() => {
+                let column_ranges = ctors.filter_map(|ctor| ctor.as_int_range()).cloned();
+                this_range.split(column_ranges).map(|(_, range)| IntRange(range)).collect()
             }
-            &Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }) => {
-                let mut split_self = SplitVarLenSlice::new(self_prefix, self_suffix, array_len);
-                let slices = ctors.filter_map(|c| c.as_slice()).map(|s| s.kind);
-                split_self.split(slices);
-                split_self.iter().map(Slice).collect()
+            Slice(this_slice @ Slice { kind: VarLen(..), .. }) => {
+                let column_slices = ctors.filter_map(|c| c.as_slice());
+                this_slice.split(column_slices).map(|(_, slice)| Slice(slice)).collect()
             }
             // Any other constructor can be used unchanged.
             _ => smallvec![self.clone()],
@@ -759,13 +815,13 @@ impl<'tcx> Constructor<'tcx> {
         match (self, other) {
             // Wildcards cover anything
             (_, Wildcard) => true,
-            // The missing ctors are not covered by anything in the matrix except wildcards.
-            (Missing { .. } | Wildcard, _) => false,
+            // Only a wildcard pattern can match these special constructors.
+            (Wildcard | Missing { .. } | NonExhaustive | Hidden, _) => false,
 
             (Single, Single) => true,
             (Variant(self_id), Variant(other_id)) => self_id == other_id,
 
-            (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range),
+            (IntRange(self_range), IntRange(other_range)) => self_range.is_subrange(other_range),
             (F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => {
                 self_from.ge(other_from)
                     && match self_to.partial_cmp(other_to) {
@@ -791,8 +847,6 @@ impl<'tcx> Constructor<'tcx> {
 
             // We are trying to inspect an opaque constant. Thus we skip the row.
             (Opaque, _) | (_, Opaque) => false,
-            // Only a wildcard pattern can match the special extra constructor.
-            (NonExhaustive, _) => false,
 
             _ => span_bug!(
                 pcx.span,
@@ -802,96 +856,121 @@ impl<'tcx> Constructor<'tcx> {
             ),
         }
     }
+}
 
-    /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is
-    /// assumed to be built from `matrix.head_ctors()` with wildcards and opaques filtered out,
-    /// and `self` is assumed to have been split from a wildcard.
-    fn is_covered_by_any<'p>(
-        &self,
-        pcx: &PatCtxt<'_, 'p, 'tcx>,
-        used_ctors: &[Constructor<'tcx>],
-    ) -> bool {
-        if used_ctors.is_empty() {
-            return false;
-        }
-
-        // This must be kept in sync with `is_covered_by`.
-        match self {
-            // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s.
-            Single => !used_ctors.is_empty(),
-            Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)),
-            IntRange(range) => used_ctors
-                .iter()
-                .filter_map(|c| c.as_int_range())
-                .any(|other| range.is_covered_by(other)),
-            Slice(slice) => used_ctors
-                .iter()
-                .filter_map(|c| c.as_slice())
-                .any(|other| slice.is_covered_by(other)),
-            // This constructor is never covered by anything else
-            NonExhaustive => false,
-            Str(..) | F32Range(..) | F64Range(..) | Opaque | Missing { .. } | Wildcard | Or => {
-                span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
-            }
-        }
-    }
+/// Describes the set of all constructors for a type.
+#[derive(Debug)]
+pub(super) enum ConstructorSet {
+    /// The type has a single constructor, e.g. `&T` or a struct.
+    Single,
+    /// This type has the following list of constructors.
+    /// Some variants are hidden, which means they won't be mentioned in diagnostics unless the user
+    /// mentioned them first. We use this for variants behind an unstable gate as well as
+    /// `#[doc(hidden)]` ones.
+    Variants {
+        visible_variants: Vec<VariantIdx>,
+        hidden_variants: Vec<VariantIdx>,
+        non_exhaustive: bool,
+    },
+    /// The type is spanned by integer values. The range or ranges give the set of allowed values.
+    /// The second range is only useful for `char`.
+    /// This is reused for bool. FIXME: don't.
+    /// `non_exhaustive` is used when the range is not allowed to be matched exhaustively (that's
+    /// for usize/isize).
+    Integers { range_1: IntRange, range_2: Option<IntRange>, non_exhaustive: bool },
+    /// The type is matched by slices. The usize is the compile-time length of the array, if known.
+    Slice(Option<usize>),
+    /// The type is matched by slices whose elements are uninhabited.
+    SliceOfEmpty,
+    /// The constructors cannot be listed, and the type cannot be matched exhaustively. E.g. `str`,
+    /// floats.
+    Unlistable,
+    /// The type has no inhabitants.
+    Uninhabited,
 }
 
-/// A wildcard constructor that we split relative to the constructors in the matrix, as explained
-/// at the top of the file.
+/// Describes the result of analyzing the constructors in a column of a match.
 ///
-/// A constructor that is not present in the matrix rows will only be covered by the rows that have
-/// wildcards. Thus we can group all of those constructors together; we call them "missing
-/// constructors". Splitting a wildcard would therefore list all present constructors individually
-/// (or grouped if they are integers or slices), and then all missing constructors together as a
-/// group.
+/// `present` is morally the set of constructors present in the column, and `missing` is the set of
+/// constructors that exist in the type but are not present in the column.
 ///
-/// However we can go further: since any constructor will match the wildcard rows, and having more
-/// rows can only reduce the amount of usefulness witnesses, we can skip the present constructors
-/// and only try the missing ones.
-/// This will not preserve the whole list of witnesses, but will preserve whether the list is empty
-/// or not. In fact this is quite natural from the point of view of diagnostics too. This is done
-/// in `to_ctors`: in some cases we only return `Missing`.
+/// More formally, they respect the following constraints:
+/// - the union of `present` and `missing` covers the whole type
+/// - `present` and `missing` are disjoint
+/// - neither contains wildcards
+/// - each constructor in `present` is covered by some non-wildcard constructor in the column
+/// - together, the constructors in `present` cover all the non-wildcard constructor in the column
+/// - non-wildcards in the column do no cover anything in `missing`
+/// - constructors in `present` and `missing` are split for the column; in other words, they are
+///     either fully included in or disjoint from each constructor in the column. This avoids
+///     non-trivial intersections like between `0..10` and `5..15`.
 #[derive(Debug)]
-pub(super) struct SplitWildcard<'tcx> {
-    /// Constructors (other than wildcards and opaques) seen in the matrix.
-    matrix_ctors: Vec<Constructor<'tcx>>,
-    /// All the constructors for this type
-    all_ctors: SmallVec<[Constructor<'tcx>; 1]>,
+struct SplitConstructorSet<'tcx> {
+    present: SmallVec<[Constructor<'tcx>; 1]>,
+    missing: Vec<Constructor<'tcx>>,
+    /// For the `non_exhaustive_omitted_patterns` lint.
+    nonexhaustive_enum_missing_visible_variants: bool,
 }
 
-impl<'tcx> SplitWildcard<'tcx> {
-    pub(super) fn new<'p>(pcx: &PatCtxt<'_, 'p, 'tcx>) -> Self {
-        debug!("SplitWildcard::new({:?})", pcx.ty);
-        let cx = pcx.cx;
-        let make_range = |start, end| {
-            IntRange(
-                // `unwrap()` is ok because we know the type is an integer.
-                IntRange::from_range(cx.tcx, start, end, pcx.ty, RangeEnd::Included),
-            )
-        };
-        // This determines the set of all possible constructors for the type `pcx.ty`. For numbers,
+impl ConstructorSet {
+    #[instrument(level = "debug", skip(cx), ret)]
+    pub(super) fn for_ty<'p, 'tcx>(cx: &MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
+        let make_range =
+            |start, end| IntRange::from_range(cx.tcx, start, end, ty, RangeEnd::Included);
+        // This determines the set of all possible constructors for the type `ty`. For numbers,
         // arrays and slices we use ranges and variable-length slices when appropriate.
         //
         // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that
         // are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the
         // returned list of constructors.
-        // Invariant: this is empty if and only if the type is uninhabited (as determined by
+        // Invariant: this is `Uninhabited` if and only if the type is uninhabited (as determined by
         // `cx.is_uninhabited()`).
-        let all_ctors = match pcx.ty.kind() {
-            ty::Bool => smallvec![make_range(0, 1)],
+        match ty.kind() {
+            ty::Bool => {
+                Self::Integers { range_1: make_range(0, 1), range_2: None, non_exhaustive: false }
+            }
+            ty::Char => {
+                // The valid Unicode Scalar Value ranges.
+                Self::Integers {
+                    range_1: make_range('\u{0000}' as u128, '\u{D7FF}' as u128),
+                    range_2: Some(make_range('\u{E000}' as u128, '\u{10FFFF}' as u128)),
+                    non_exhaustive: false,
+                }
+            }
+            &ty::Int(ity) => {
+                // `usize`/`isize` are not allowed to be matched exhaustively unless the
+                // `precise_pointer_size_matching` feature is enabled.
+                let non_exhaustive =
+                    ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching;
+                let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128;
+                let min = 1u128 << (bits - 1);
+                let max = min - 1;
+                Self::Integers { range_1: make_range(min, max), non_exhaustive, range_2: None }
+            }
+            &ty::Uint(uty) => {
+                // `usize`/`isize` are not allowed to be matched exhaustively unless the
+                // `precise_pointer_size_matching` feature is enabled.
+                let non_exhaustive =
+                    ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching;
+                let size = Integer::from_uint_ty(&cx.tcx, uty).size();
+                let max = size.truncate(u128::MAX);
+                Self::Integers { range_1: make_range(0, max), non_exhaustive, range_2: None }
+            }
             ty::Array(sub_ty, len) if len.try_eval_target_usize(cx.tcx, cx.param_env).is_some() => {
                 let len = len.eval_target_usize(cx.tcx, cx.param_env) as usize;
                 if len != 0 && cx.is_uninhabited(*sub_ty) {
-                    smallvec![]
+                    Self::Uninhabited
                 } else {
-                    smallvec![Slice(Slice::new(Some(len), VarLen(0, 0)))]
+                    Self::Slice(Some(len))
                 }
             }
             // Treat arrays of a constant but unknown length like slices.
             ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
-                let kind = if cx.is_uninhabited(*sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
-                smallvec![Slice(Slice::new(None, kind))]
+                if cx.is_uninhabited(*sub_ty) {
+                    Self::SliceOfEmpty
+                } else {
+                    Self::Slice(None)
+                }
             }
             ty::Adt(def, args) if def.is_enum() => {
                 // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
@@ -910,19 +989,14 @@ impl<'tcx> SplitWildcard<'tcx> {
                 //
                 // we don't want to show every possible IO error, but instead have only `_` as the
                 // witness.
-                let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
-
-                let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
+                let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
 
-                // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
-                // as though it had an "unknown" constructor to avoid exposing its emptiness. The
-                // exception is if the pattern is at the top level, because we want empty matches to be
-                // considered exhaustive.
-                let is_secretly_empty =
-                    def.variants().is_empty() && !is_exhaustive_pat_feature && !pcx.is_top_level;
-
-                let mut ctors: SmallVec<[_; 1]> =
-                    def.variants()
+                if def.variants().is_empty() && !is_declared_nonexhaustive {
+                    Self::Uninhabited
+                } else {
+                    let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
+                    let (hidden_variants, visible_variants) = def
+                        .variants()
                         .iter_enumerated()
                         .filter(|(_, v)| {
                             // If `exhaustive_patterns` is enabled, we exclude variants known to be
@@ -932,135 +1006,173 @@ impl<'tcx> SplitWildcard<'tcx> {
                                     .instantiate(cx.tcx, args)
                                     .apply(cx.tcx, cx.param_env, cx.module)
                         })
-                        .map(|(idx, _)| Variant(idx))
-                        .collect();
+                        .map(|(idx, _)| idx)
+                        .partition(|idx| {
+                            let variant_def_id = def.variant(*idx).def_id;
+                            // Filter variants that depend on a disabled unstable feature.
+                            let is_unstable = matches!(
+                                cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
+                                EvalResult::Deny { .. }
+                            );
+                            // Filter foreign `#[doc(hidden)]` variants.
+                            let is_doc_hidden =
+                                cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
+                            is_unstable || is_doc_hidden
+                        });
+
+                    Self::Variants {
+                        visible_variants,
+                        hidden_variants,
+                        non_exhaustive: is_declared_nonexhaustive,
+                    }
+                }
+            }
+            ty::Never => Self::Uninhabited,
+            _ if cx.is_uninhabited(ty) => Self::Uninhabited,
+            ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => Self::Single,
+            // This type is one for which we cannot list constructors, like `str` or `f64`.
+            _ => Self::Unlistable,
+        }
+    }
 
-                if is_secretly_empty || is_declared_nonexhaustive {
-                    ctors.push(NonExhaustive);
+    /// This is the core logical operation of exhaustiveness checking. This analyzes a column a
+    /// constructors to 1/ determine which constructors of the type (if any) are missing; 2/ split
+    /// constructors to handle non-trivial intersections e.g. on ranges or slices.
+    #[instrument(level = "debug", skip(self, pcx, ctors), ret)]
+    fn split<'a, 'tcx>(
+        &self,
+        pcx: &PatCtxt<'_, '_, 'tcx>,
+        ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
+    ) -> SplitConstructorSet<'tcx>
+    where
+        'tcx: 'a,
+    {
+        let mut present: SmallVec<[_; 1]> = SmallVec::new();
+        let mut missing = Vec::new();
+        // Constructors in `ctors`, except wildcards.
+        let mut seen = ctors.filter(|c| !(matches!(c, Opaque | Wildcard)));
+        let mut nonexhaustive_enum_missing_visible_variants = false;
+        match self {
+            ConstructorSet::Single => {
+                if seen.next().is_none() {
+                    missing.push(Single);
+                } else {
+                    present.push(Single);
                 }
-                ctors
             }
-            ty::Char => {
-                smallvec![
-                    // The valid Unicode Scalar Value ranges.
-                    make_range('\u{0000}' as u128, '\u{D7FF}' as u128),
-                    make_range('\u{E000}' as u128, '\u{10FFFF}' as u128),
-                ]
+            ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
+                let seen_set: FxHashSet<_> = seen.map(|c| c.as_variant().unwrap()).collect();
+                let mut skipped_a_hidden_variant = false;
+                for variant in visible_variants {
+                    let ctor = Variant(*variant);
+                    if seen_set.contains(&variant) {
+                        present.push(ctor);
+                    } else {
+                        missing.push(ctor);
+                    }
+                }
+                nonexhaustive_enum_missing_visible_variants =
+                    *non_exhaustive && !missing.is_empty();
+
+                for variant in hidden_variants {
+                    let ctor = Variant(*variant);
+                    if seen_set.contains(&variant) {
+                        present.push(ctor);
+                    } else {
+                        skipped_a_hidden_variant = true;
+                    }
+                }
+                if skipped_a_hidden_variant {
+                    missing.push(Hidden);
+                }
+
+                if *non_exhaustive {
+                    missing.push(NonExhaustive);
+                }
             }
-            ty::Int(_) | ty::Uint(_)
-                if pcx.ty.is_ptr_sized_integral()
-                    && !cx.tcx.features().precise_pointer_size_matching =>
-            {
-                // `usize`/`isize` are not allowed to be matched exhaustively unless the
-                // `precise_pointer_size_matching` feature is enabled. So we treat those types like
-                // `#[non_exhaustive]` enums by returning a special unmatchable constructor.
-                smallvec![NonExhaustive]
+            ConstructorSet::Integers { range_1, range_2, non_exhaustive } => {
+                let seen_ranges: Vec<_> =
+                    seen.map(|ctor| ctor.as_int_range().unwrap().clone()).collect();
+                for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) {
+                    match seen {
+                        Presence::Unseen => missing.push(IntRange(splitted_range)),
+                        Presence::Seen => present.push(IntRange(splitted_range)),
+                    }
+                }
+                if let Some(range_2) = range_2 {
+                    for (seen, splitted_range) in range_2.split(seen_ranges.into_iter()) {
+                        match seen {
+                            Presence::Unseen => missing.push(IntRange(splitted_range)),
+                            Presence::Seen => present.push(IntRange(splitted_range)),
+                        }
+                    }
+                }
+
+                if *non_exhaustive {
+                    missing.push(NonExhaustive);
+                }
             }
-            &ty::Int(ity) => {
-                let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128;
-                let min = 1u128 << (bits - 1);
-                let max = min - 1;
-                smallvec![make_range(min, max)]
+            &ConstructorSet::Slice(array_len) => {
+                let seen_slices = seen.map(|c| c.as_slice().unwrap());
+                let base_slice = Slice::new(array_len, VarLen(0, 0));
+                for (seen, splitted_slice) in base_slice.split(seen_slices) {
+                    let ctor = Slice(splitted_slice);
+                    match seen {
+                        Presence::Unseen => missing.push(ctor),
+                        Presence::Seen => present.push(ctor),
+                    }
+                }
             }
-            &ty::Uint(uty) => {
-                let size = Integer::from_uint_ty(&cx.tcx, uty).size();
-                let max = size.truncate(u128::MAX);
-                smallvec![make_range(0, max)]
+            ConstructorSet::SliceOfEmpty => {
+                // This one is tricky because even though there's only one possible value of this
+                // type (namely `[]`), slice patterns of all lengths are allowed, they're just
+                // unreachable if length != 0.
+                // We still gather the seen constructors in `present`, but the only slice that can
+                // go in `missing` is `[]`.
+                let seen_slices = seen.map(|c| c.as_slice().unwrap());
+                let base_slice = Slice::new(None, VarLen(0, 0));
+                for (seen, splitted_slice) in base_slice.split(seen_slices) {
+                    let ctor = Slice(splitted_slice);
+                    match seen {
+                        Presence::Seen => present.push(ctor),
+                        Presence::Unseen if splitted_slice.arity() == 0 => {
+                            missing.push(Slice(Slice::new(None, FixedLen(0))))
+                        }
+                        Presence::Unseen => {}
+                    }
+                }
+            }
+            ConstructorSet::Unlistable => {
+                // Since we can't list constructors, we take the ones in the column. This might list
+                // some constructors several times but there's not much we can do.
+                present.extend(seen.cloned());
+                missing.push(NonExhaustive);
             }
-            // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot
+            // If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot
             // expose its emptiness. The exception is if the pattern is at the top level, because we
             // want empty matches to be considered exhaustive.
-            ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => {
-                smallvec![NonExhaustive]
+            ConstructorSet::Uninhabited
+                if !pcx.cx.tcx.features().exhaustive_patterns && !pcx.is_top_level =>
+            {
+                missing.push(NonExhaustive);
             }
-            ty::Never => smallvec![],
-            _ if cx.is_uninhabited(pcx.ty) => smallvec![],
-            ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => smallvec![Single],
-            // This type is one for which we cannot list constructors, like `str` or `f64`.
-            _ => smallvec![NonExhaustive],
-        };
+            ConstructorSet::Uninhabited => {}
+        }
 
-        SplitWildcard { matrix_ctors: Vec::new(), all_ctors }
+        SplitConstructorSet { present, missing, nonexhaustive_enum_missing_visible_variants }
     }
 
-    /// Pass a set of constructors relative to which to split this one. Don't call twice, it won't
-    /// do what you want.
-    pub(super) fn split<'a>(
-        &mut self,
+    /// Compute the set of constructors missing from this column.
+    /// This is only used for reporting to the user.
+    pub(super) fn compute_missing<'a, 'tcx>(
+        &self,
         pcx: &PatCtxt<'_, '_, 'tcx>,
         ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
-    ) where
+    ) -> Vec<Constructor<'tcx>>
+    where
         'tcx: 'a,
     {
-        // Since `all_ctors` never contains wildcards, this won't recurse further.
-        self.all_ctors =
-            self.all_ctors.iter().flat_map(|ctor| ctor.split(pcx, ctors.clone())).collect();
-        self.matrix_ctors = ctors.filter(|c| !matches!(c, Wildcard | Opaque)).cloned().collect();
-    }
-
-    /// Whether there are any value constructors for this type that are not present in the matrix.
-    fn any_missing(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
-        self.iter_missing(pcx).next().is_some()
-    }
-
-    /// Iterate over the constructors for this type that are not present in the matrix.
-    pub(super) fn iter_missing<'a, 'p>(
-        &'a self,
-        pcx: &'a PatCtxt<'a, 'p, 'tcx>,
-    ) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'p> {
-        self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.matrix_ctors))
-    }
-
-    /// Return the set of constructors resulting from splitting the wildcard. As explained at the
-    /// top of the file, if any constructors are missing we can ignore the present ones.
-    fn into_ctors(self, pcx: &PatCtxt<'_, '_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> {
-        if self.any_missing(pcx) {
-            // Some constructors are missing, thus we can specialize with the special `Missing`
-            // constructor, which stands for those constructors that are not seen in the matrix,
-            // and matches the same rows as any of them (namely the wildcard rows). See the top of
-            // the file for details.
-            // However, when all constructors are missing we can also specialize with the full
-            // `Wildcard` constructor. The difference will depend on what we want in diagnostics.
-
-            // If some constructors are missing, we typically want to report those constructors,
-            // e.g.:
-            // ```
-            //     enum Direction { N, S, E, W }
-            //     let Direction::N = ...;
-            // ```
-            // we can report 3 witnesses: `S`, `E`, and `W`.
-            //
-            // However, if the user didn't actually specify a constructor
-            // in this arm, e.g., in
-            // ```
-            //     let x: (Direction, Direction, bool) = ...;
-            //     let (_, _, false) = x;
-            // ```
-            // we don't want to show all 16 possible witnesses `(<direction-1>, <direction-2>,
-            // true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we
-            // prefer to report just a wildcard `_`.
-            //
-            // The exception is: if we are at the top-level, for example in an empty match, we
-            // sometimes prefer reporting the list of constructors instead of just `_`.
-            let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
-            let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing {
-                if pcx.is_non_exhaustive {
-                    Missing {
-                        nonexhaustive_enum_missing_real_variants: self
-                            .iter_missing(pcx)
-                            .any(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx))),
-                    }
-                } else {
-                    Missing { nonexhaustive_enum_missing_real_variants: false }
-                }
-            } else {
-                Wildcard
-            };
-            return smallvec![ctor];
-        }
-
-        // All the constructors are present in the matrix, so we just go through them all.
-        self.all_ctors
+        self.split(pcx, ctors).missing
     }
 }
 
@@ -1177,8 +1289,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
             | F32Range(..)
             | F64Range(..)
             | IntRange(..)
-            | NonExhaustive
             | Opaque
+            | NonExhaustive
+            | Hidden
             | Missing { .. }
             | Wildcard => Fields::empty(),
             Or => {
@@ -1492,7 +1605,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
             }
             &Str(value) => PatKind::Constant { value },
             IntRange(range) => return range.to_pat(cx.tcx, self.ty),
-            Wildcard | NonExhaustive => PatKind::Wild,
+            Wildcard | NonExhaustive | Hidden => PatKind::Wild,
             Missing { .. } => bug!(
                 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
                 `Missing` should have been processed in `apply_constructors`"
@@ -1675,15 +1788,15 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
             F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
             F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
             IntRange(range) => write!(f, "{range:?}"), // Best-effort, will render e.g. `false` as `0..=0`
-            Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty),
+            Str(value) => write!(f, "{value}"),
+            Opaque => write!(f, "<constant pattern>"),
             Or => {
                 for pat in self.iter_fields() {
                     write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
                 }
                 Ok(())
             }
-            Str(value) => write!(f, "{value}"),
-            Opaque => write!(f, "<constant pattern>"),
+            Wildcard | Missing { .. } | NonExhaustive | Hidden => write!(f, "_ : {:?}", self.ty),
         }
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index fe47a1cd78c..25726c5a872 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -20,15 +20,15 @@ use rustc_index::Idx;
 use rustc_middle::mir::interpret::{
     ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
 };
-use rustc_middle::mir::{self, Const, UserTypeProjection};
-use rustc_middle::mir::{BorrowKind, Mutability};
+use rustc_middle::mir::{self, BorrowKind, Const, Mutability, UserTypeProjection};
 use rustc_middle::thir::{Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange};
-use rustc_middle::ty::CanonicalUserTypeAnnotation;
-use rustc_middle::ty::TypeVisitableExt;
-use rustc_middle::ty::{self, AdtDef, Region, Ty, TyCtxt, UserType};
-use rustc_middle::ty::{GenericArg, GenericArgsRef};
-use rustc_span::{Span, Symbol};
-use rustc_target::abi::FieldIdx;
+use rustc_middle::ty::layout::IntegerExt;
+use rustc_middle::ty::{
+    self, AdtDef, CanonicalUserTypeAnnotation, GenericArg, GenericArgsRef, Region, Ty, TyCtxt,
+    TypeVisitableExt, UserType,
+};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
+use rustc_target::abi::{FieldIdx, Integer};
 
 use std::cmp::Ordering;
 
@@ -85,127 +85,159 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         )
     }
 
-    fn lower_range_expr(
+    fn lower_pattern_range_endpoint(
         &mut self,
-        expr: &'tcx hir::Expr<'tcx>,
-    ) -> (PatKind<'tcx>, Option<Ascription<'tcx>>) {
-        match self.lower_lit(expr) {
-            PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => {
-                (kind, Some(ascription))
+        expr: Option<&'tcx hir::Expr<'tcx>>,
+    ) -> Result<(Option<mir::Const<'tcx>>, Option<Ascription<'tcx>>), ErrorGuaranteed> {
+        match expr {
+            None => Ok((None, None)),
+            Some(expr) => {
+                let (kind, ascr) = match self.lower_lit(expr) {
+                    PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => {
+                        (kind, Some(ascription))
+                    }
+                    kind => (kind, None),
+                };
+                let value = if let PatKind::Constant { value } = kind {
+                    value
+                } else {
+                    let msg = format!(
+                        "found bad range pattern endpoint `{expr:?}` outside of error recovery"
+                    );
+                    return Err(self.tcx.sess.delay_span_bug(expr.span, msg));
+                };
+                Ok((Some(value), ascr))
             }
-            kind => (kind, None),
         }
     }
 
+    /// Overflowing literals are linted against in a late pass. This is mostly fine, except when we
+    /// encounter a range pattern like `-130i8..2`: if we believe `eval_bits`, this looks like a
+    /// range where the endpoints are in the wrong order. To avoid a confusing error message, we
+    /// check for overflow then.
+    /// This is only called when the range is already known to be malformed.
+    fn error_on_literal_overflow(
+        &self,
+        expr: Option<&'tcx hir::Expr<'tcx>>,
+        ty: Ty<'tcx>,
+    ) -> Result<(), ErrorGuaranteed> {
+        use hir::{ExprKind, UnOp};
+        use rustc_ast::ast::LitKind;
+
+        let Some(mut expr) = expr else {
+            return Ok(());
+        };
+        let span = expr.span;
+
+        // We need to inspect the original expression, because if we only inspect the output of
+        // `eval_bits`, an overflowed value has already been wrapped around.
+        // We mostly copy the logic from the `rustc_lint::OVERFLOWING_LITERALS` lint.
+        let mut negated = false;
+        if let ExprKind::Unary(UnOp::Neg, sub_expr) = expr.kind {
+            negated = true;
+            expr = sub_expr;
+        }
+        let ExprKind::Lit(lit) = expr.kind else {
+            return Ok(());
+        };
+        let LitKind::Int(lit_val, _) = lit.node else {
+            return Ok(());
+        };
+        let (min, max): (i128, u128) = match ty.kind() {
+            ty::Int(ity) => {
+                let size = Integer::from_int_ty(&self.tcx, *ity).size();
+                (size.signed_int_min(), size.signed_int_max() as u128)
+            }
+            ty::Uint(uty) => {
+                let size = Integer::from_uint_ty(&self.tcx, *uty).size();
+                (0, size.unsigned_int_max())
+            }
+            _ => {
+                return Ok(());
+            }
+        };
+        // Detect literal value out of range `[min, max]` inclusive, avoiding use of `-min` to
+        // prevent overflow/panic.
+        if (negated && lit_val > max + 1) || (!negated && lit_val > max) {
+            return Err(self.tcx.sess.emit_err(LiteralOutOfRange { span, ty, min, max }));
+        }
+        Ok(())
+    }
+
     fn lower_pattern_range(
         &mut self,
-        ty: Ty<'tcx>,
-        lo: mir::Const<'tcx>,
-        hi: mir::Const<'tcx>,
+        lo_expr: Option<&'tcx hir::Expr<'tcx>>,
+        hi_expr: Option<&'tcx hir::Expr<'tcx>>,
         end: RangeEnd,
+        ty: Ty<'tcx>,
         span: Span,
-        lo_expr: Option<&hir::Expr<'tcx>>,
-        hi_expr: Option<&hir::Expr<'tcx>>,
-    ) -> PatKind<'tcx> {
+    ) -> Result<PatKind<'tcx>, ErrorGuaranteed> {
+        if lo_expr.is_none() && hi_expr.is_none() {
+            let msg = format!("found twice-open range pattern (`..`) outside of error recovery");
+            return Err(self.tcx.sess.delay_span_bug(span, msg));
+        }
+
+        let (lo, lo_ascr) = self.lower_pattern_range_endpoint(lo_expr)?;
+        let (hi, hi_ascr) = self.lower_pattern_range_endpoint(hi_expr)?;
+
+        let lo = lo.unwrap_or_else(|| {
+            // Unwrap is ok because the type is known to be numeric.
+            let lo = ty.numeric_min_val(self.tcx).unwrap();
+            mir::Const::from_ty_const(lo, self.tcx)
+        });
+        let hi = hi.unwrap_or_else(|| {
+            // Unwrap is ok because the type is known to be numeric.
+            let hi = ty.numeric_max_val(self.tcx).unwrap();
+            mir::Const::from_ty_const(hi, self.tcx)
+        });
         assert_eq!(lo.ty(), ty);
         assert_eq!(hi.ty(), ty);
+
         let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env);
-        let max = || {
-            self.tcx
-                .layout_of(self.param_env.with_reveal_all_normalized(self.tcx).and(ty))
-                .ok()
-                .unwrap()
-                .size
-                .unsigned_int_max()
-        };
-        match (end, cmp) {
+        let mut kind = match (end, cmp) {
             // `x..y` where `x < y`.
             // Non-empty because the range includes at least `x`.
             (RangeEnd::Excluded, Some(Ordering::Less)) => {
                 PatKind::Range(Box::new(PatRange { lo, hi, end }))
             }
-            // `x..y` where `x >= y`. The range is empty => error.
-            (RangeEnd::Excluded, _) => {
-                let mut lower_overflow = false;
-                let mut higher_overflow = false;
-                if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr
-                    && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
-                {
-                    if lo.eval_bits(self.tcx, self.param_env) != val {
-                        lower_overflow = true;
-                        self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
-                    }
-                }
-                if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr
-                    && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
-                {
-                    if hi.eval_bits(self.tcx, self.param_env) != val {
-                        higher_overflow = true;
-                        self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
-                    }
-                }
-                if !lower_overflow && !higher_overflow {
-                    self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span });
-                }
-                PatKind::Wild
-            }
             // `x..=y` where `x == y`.
             (RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo },
             // `x..=y` where `x < y`.
             (RangeEnd::Included, Some(Ordering::Less)) => {
                 PatKind::Range(Box::new(PatRange { lo, hi, end }))
             }
-            // `x..=y` where `x > y` hence the range is empty => error.
-            (RangeEnd::Included, _) => {
-                let mut lower_overflow = false;
-                let mut higher_overflow = false;
-                if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr
-                    && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
-                {
-                    if lo.eval_bits(self.tcx, self.param_env) != val {
-                        lower_overflow = true;
-                        self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
+            // `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
+            _ => {
+                // Emit a more appropriate message if there was overflow.
+                self.error_on_literal_overflow(lo_expr, ty)?;
+                self.error_on_literal_overflow(hi_expr, ty)?;
+                let e = match end {
+                    RangeEnd::Included => {
+                        self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
+                            span,
+                            teach: self.tcx.sess.teach(&error_code!(E0030)).then_some(()),
+                        })
                     }
-                }
-                if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr
-                    && let rustc_ast::ast::LitKind::Int(val, _) = lit.node
-                {
-                    if hi.eval_bits(self.tcx, self.param_env) != val {
-                        higher_overflow = true;
-                        self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() });
+                    RangeEnd::Excluded => {
+                        self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span })
                     }
-                }
-                if !lower_overflow && !higher_overflow {
-                    self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
-                        span,
-                        teach: self.tcx.sess.teach(&error_code!(E0030)).then_some(()),
-                    });
-                }
-                PatKind::Wild
+                };
+                return Err(e);
             }
-        }
-    }
+        };
 
-    fn normalize_range_pattern_ends(
-        &self,
-        ty: Ty<'tcx>,
-        lo: Option<&PatKind<'tcx>>,
-        hi: Option<&PatKind<'tcx>>,
-    ) -> Option<(mir::Const<'tcx>, mir::Const<'tcx>)> {
-        match (lo, hi) {
-            (Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => {
-                Some((*lo, *hi))
-            }
-            (Some(PatKind::Constant { value: lo }), None) => {
-                let hi = ty.numeric_max_val(self.tcx)?;
-                Some((*lo, mir::Const::from_ty_const(hi, self.tcx)))
-            }
-            (None, Some(PatKind::Constant { value: hi })) => {
-                let lo = ty.numeric_min_val(self.tcx)?;
-                Some((mir::Const::from_ty_const(lo, self.tcx), *hi))
+        // If we are handling a range with associated constants (e.g.
+        // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
+        // constants somewhere. Have them on the range pattern.
+        for ascr in [lo_ascr, hi_ascr] {
+            if let Some(ascription) = ascr {
+                kind = PatKind::AscribeUserType {
+                    ascription,
+                    subpattern: Box::new(Pat { span, ty, kind }),
+                };
             }
-            _ => None,
         }
+        Ok(kind)
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -220,37 +252,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
             hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => {
                 let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref());
-                let lo_span = lo_expr.map_or(pat.span, |e| e.span);
-                let lo = lo_expr.map(|e| self.lower_range_expr(e));
-                let hi = hi_expr.map(|e| self.lower_range_expr(e));
-
-                let (lp, hp) = (lo.as_ref().map(|(x, _)| x), hi.as_ref().map(|(x, _)| x));
-                let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) {
-                    Some((lc, hc)) => {
-                        self.lower_pattern_range(ty, lc, hc, end, lo_span, lo_expr, hi_expr)
-                    }
-                    None => {
-                        let msg = format!(
-                            "found bad range pattern `{:?}` outside of error recovery",
-                            (&lo, &hi),
-                        );
-                        self.tcx.sess.delay_span_bug(pat.span, msg);
-                        PatKind::Wild
-                    }
-                };
-
-                // If we are handling a range with associated constants (e.g.
-                // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
-                // constants somewhere. Have them on the range pattern.
-                for end in &[lo, hi] {
-                    if let Some((_, Some(ascription))) = end {
-                        let subpattern = Box::new(Pat { span: pat.span, ty, kind });
-                        kind =
-                            PatKind::AscribeUserType { ascription: ascription.clone(), subpattern };
-                    }
-                }
-
-                kind
+                // FIXME?: returning `_` can cause inaccurate "unreachable" warnings. This can be
+                // fixed by returning `PatKind::Const(ConstKind::Error(...))` if #115937 gets
+                // merged.
+                self.lower_pattern_range(lo_expr, hi_expr, end, ty, span).unwrap_or(PatKind::Wild)
             }
 
             hir::PatKind::Path(ref qpath) => {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 21031e8ba9d..6cd73c7eaa9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -307,7 +307,7 @@
 
 use self::ArmType::*;
 use self::Usefulness::*;
-use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
+use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, Fields};
 use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
 
 use rustc_data_structures::captures::Captures;
@@ -368,8 +368,6 @@ pub(super) struct PatCtxt<'a, 'p, 'tcx> {
     /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
     /// subpattern.
     pub(super) is_top_level: bool,
-    /// Whether the current pattern is from a `non_exhaustive` enum.
-    pub(super) is_non_exhaustive: bool,
 }
 
 impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
@@ -616,62 +614,41 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
             WithWitnesses(ref witnesses) if witnesses.is_empty() => self,
             WithWitnesses(witnesses) => {
                 let new_witnesses = if let Constructor::Missing { .. } = ctor {
+                    let mut missing = ConstructorSet::for_ty(pcx.cx, pcx.ty)
+                        .compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
+                    if missing.iter().any(|c| c.is_non_exhaustive()) {
+                        // We only report `_` here; listing other constructors would be redundant.
+                        missing = vec![Constructor::NonExhaustive];
+                    }
+
                     // We got the special `Missing` constructor, so each of the missing constructors
-                    // gives a new pattern that is not caught by the match. We list those patterns.
-                    if pcx.is_non_exhaustive {
-                        witnesses
-                            .into_iter()
-                            // Here we don't want the user to try to list all variants, we want them to add
-                            // a wildcard, so we only suggest that.
-                            .map(|witness| {
-                                witness.apply_constructor(pcx, &Constructor::NonExhaustive)
-                            })
-                            .collect()
-                    } else {
-                        let mut split_wildcard = SplitWildcard::new(pcx);
-                        split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
-
-                        // This lets us know if we skipped any variants because they are marked
-                        // `doc(hidden)` or they are unstable feature gate (only stdlib types).
-                        let mut hide_variant_show_wild = false;
-                        // Construct for each missing constructor a "wild" version of this
-                        // constructor, that matches everything that can be built with
-                        // it. For example, if `ctor` is a `Constructor::Variant` for
-                        // `Option::Some`, we get the pattern `Some(_)`.
-                        let mut new_patterns: Vec<DeconstructedPat<'_, '_>> = split_wildcard
-                            .iter_missing(pcx)
-                            .filter_map(|missing_ctor| {
-                                // Check if this variant is marked `doc(hidden)`
-                                if missing_ctor.is_doc_hidden_variant(pcx)
-                                    || missing_ctor.is_unstable_variant(pcx)
-                                {
-                                    hide_variant_show_wild = true;
-                                    return None;
-                                }
-                                Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone()))
-                            })
-                            .collect();
-
-                        if hide_variant_show_wild {
-                            new_patterns.push(DeconstructedPat::wildcard(pcx.ty, pcx.span));
-                        }
-
-                        witnesses
-                            .into_iter()
-                            .flat_map(|witness| {
-                                new_patterns.iter().map(move |pat| {
-                                    Witness(
-                                        witness
-                                            .0
-                                            .iter()
-                                            .chain(once(pat))
-                                            .map(DeconstructedPat::clone_and_forget_reachability)
-                                            .collect(),
-                                    )
-                                })
+                    // gives a new pattern that is not caught by the match.
+                    // We construct for each missing constructor a version of this constructor with
+                    // wildcards for fields, i.e. that matches everything that can be built with it.
+                    // For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get
+                    // the pattern `Some(_)`.
+                    let new_patterns: Vec<DeconstructedPat<'_, '_>> = missing
+                        .into_iter()
+                        .map(|missing_ctor| {
+                            DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone())
+                        })
+                        .collect();
+
+                    witnesses
+                        .into_iter()
+                        .flat_map(|witness| {
+                            new_patterns.iter().map(move |pat| {
+                                Witness(
+                                    witness
+                                        .0
+                                        .iter()
+                                        .chain(once(pat))
+                                        .map(DeconstructedPat::clone_and_forget_reachability)
+                                        .collect(),
+                                )
                             })
-                            .collect()
-                    }
+                        })
+                        .collect()
                 } else {
                     witnesses
                         .into_iter()
@@ -844,9 +821,8 @@ fn is_useful<'p, 'tcx>(
                 ty = row.head().ty();
             }
         }
-        let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
         debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
-        let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
+        let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level };
 
         let v_ctor = v.head().ctor();
         debug!(?v_ctor);
@@ -861,7 +837,8 @@ fn is_useful<'p, 'tcx>(
         }
         // We split the head constructor of `v`.
         let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
-        let is_non_exhaustive_and_wild = is_non_exhaustive && v_ctor.is_wildcard();
+        let is_non_exhaustive_and_wild =
+            cx.is_foreign_non_exhaustive_enum(ty) && v_ctor.is_wildcard();
         // For each constructor, we compute whether there's a value that starts with it that would
         // witness the usefulness of `v`.
         let start_matrix = &matrix;
@@ -895,27 +872,21 @@ fn is_useful<'p, 'tcx>(
                 && usefulness.is_useful() && matches!(witness_preference, RealArm)
                 && matches!(
                     &ctor,
-                    Constructor::Missing { nonexhaustive_enum_missing_real_variants: true }
+                    Constructor::Missing { nonexhaustive_enum_missing_visible_variants: true }
                 )
             {
-                let patterns = {
-                    let mut split_wildcard = SplitWildcard::new(pcx);
-                    split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
-                    // Construct for each missing constructor a "wild" version of this
-                    // constructor, that matches everything that can be built with
-                    // it. For example, if `ctor` is a `Constructor::Variant` for
-                    // `Option::Some`, we get the pattern `Some(_)`.
-                    split_wildcard
-                        .iter_missing(pcx)
-                        // Filter out the `NonExhaustive` because we want to list only real
-                        // variants. Also remove any unstable feature gated variants.
-                        // Because of how we computed `nonexhaustive_enum_missing_real_variants`,
-                        // this will not return an empty `Vec`.
-                        .filter(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx)))
-                        .cloned()
-                        .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
-                        .collect::<Vec<_>>()
-                };
+                let missing = ConstructorSet::for_ty(pcx.cx, pcx.ty)
+                    .compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
+                // Construct for each missing constructor a "wild" version of this constructor, that
+                // matches everything that can be built with it. For example, if `ctor` is a
+                // `Constructor::Variant` for `Option::Some`, we get the pattern `Some(_)`.
+                let patterns = missing
+                    .into_iter()
+                    // Because of how we computed `nonexhaustive_enum_missing_visible_variants`,
+                    // this will not return an empty `Vec`.
+                    .filter(|c| !(matches!(c, Constructor::NonExhaustive | Constructor::Hidden)))
+                    .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
+                    .collect::<Vec<_>>();
 
                 // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
                 // is not exhaustive enough.
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 4fc78b28580..d379db5e07d 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -2,8 +2,6 @@
 //! assertion failures
 
 use either::Right;
-
-use rustc_const_eval::const_eval::CheckAlignment;
 use rustc_const_eval::ReportErrorExt;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::DefKind;
@@ -16,7 +14,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::{self, GenericArgs, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::{def_id::DefId, Span};
-use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout};
+use rustc_target::abi::{self, HasDataLayout, Size, TargetDataLayout};
 use rustc_target::spec::abi::Abi as CallAbi;
 
 use crate::dataflow_const_prop::Patch;
@@ -141,27 +139,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
     type MemoryKind = !;
 
     #[inline(always)]
-    fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
-        // We do not check for alignment to avoid having to carry an `Align`
-        // in `ConstValue::Indirect`.
-        CheckAlignment::No
+    fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        false // no reason to enforce alignment
     }
 
     #[inline(always)]
     fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
         false // for now, we don't enforce validity
     }
-    fn alignment_check_failed(
-        ecx: &InterpCx<'mir, 'tcx, Self>,
-        _has: Align,
-        _required: Align,
-        _check: CheckAlignment,
-    ) -> InterpResult<'tcx, ()> {
-        span_bug!(
-            ecx.cur_span(),
-            "`alignment_check_failed` called when no alignment check requested"
-        )
-    }
 
     fn load_mir(
         _ecx: &InterpCx<'mir, 'tcx, Self>,
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 64e262c6c93..b51bfe8c6ab 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -22,7 +22,6 @@ use rustc_middle::ty::{
 };
 use rustc_span::Span;
 use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
-use rustc_trait_selection::traits;
 
 use crate::const_prop::CanConstProp;
 use crate::const_prop::ConstPropMachine;
@@ -35,9 +34,9 @@ use crate::MirLint;
 /// Severely regress performance.
 const MAX_ALLOC_LIMIT: u64 = 1024;
 
-pub struct ConstProp;
+pub struct ConstPropLint;
 
-impl<'tcx> MirLint<'tcx> for ConstProp {
+impl<'tcx> MirLint<'tcx> for ConstPropLint {
     fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
         if body.tainted_by_errors.is_some() {
             return;
@@ -49,61 +48,25 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
         }
 
         let def_id = body.source.def_id().expect_local();
-        let is_fn_like = tcx.def_kind(def_id).is_fn_like();
-        let is_assoc_const = tcx.def_kind(def_id) == DefKind::AssocConst;
+        let def_kind = tcx.def_kind(def_id);
+        let is_fn_like = def_kind.is_fn_like();
+        let is_assoc_const = def_kind == DefKind::AssocConst;
 
         // Only run const prop on functions, methods, closures and associated constants
         if !is_fn_like && !is_assoc_const {
             // skip anon_const/statics/consts because they'll be evaluated by miri anyway
-            trace!("ConstProp skipped for {:?}", def_id);
+            trace!("ConstPropLint skipped for {:?}", def_id);
             return;
         }
 
-        let is_generator = tcx.type_of(def_id.to_def_id()).instantiate_identity().is_generator();
         // FIXME(welseywiser) const prop doesn't work on generators because of query cycles
         // computing their layout.
-        if is_generator {
-            trace!("ConstProp skipped for generator {:?}", def_id);
+        if let DefKind::Generator = def_kind {
+            trace!("ConstPropLint skipped for generator {:?}", def_id);
             return;
         }
 
-        // Check if it's even possible to satisfy the 'where' clauses
-        // for this item.
-        // This branch will never be taken for any normal function.
-        // However, it's possible to `#!feature(trivial_bounds)]` to write
-        // a function with impossible to satisfy clauses, e.g.:
-        // `fn foo() where String: Copy {}`
-        //
-        // We don't usually need to worry about this kind of case,
-        // since we would get a compilation error if the user tried
-        // to call it. However, since we can do const propagation
-        // even without any calls to the function, we need to make
-        // sure that it even makes sense to try to evaluate the body.
-        // If there are unsatisfiable where clauses, then all bets are
-        // off, and we just give up.
-        //
-        // We manually filter the predicates, skipping anything that's not
-        // "global". We are in a potentially generic context
-        // (e.g. we are evaluating a function without substituting generic
-        // parameters, so this filtering serves two purposes:
-        //
-        // 1. We skip evaluating any predicates that we would
-        // never be able prove are unsatisfiable (e.g. `<T as Foo>`
-        // 2. We avoid trying to normalize predicates involving generic
-        // parameters (e.g. `<T as Foo>::MyItem`). This can confuse
-        // the normalization code (leading to cycle errors), since
-        // it's usually never invoked in this way.
-        let predicates = tcx
-            .predicates_of(def_id.to_def_id())
-            .predicates
-            .iter()
-            .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
-        if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
-            trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
-            return;
-        }
-
-        trace!("ConstProp starting for {:?}", def_id);
+        trace!("ConstPropLint starting for {:?}", def_id);
 
         // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
         // constants, instead of just checking for const-folding succeeding.
@@ -112,7 +75,7 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
         let mut linter = ConstPropagator::new(body, tcx);
         linter.visit_body(body);
 
-        trace!("ConstProp done for {:?}", def_id);
+        trace!("ConstPropLint done for {:?}", def_id);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index 78845af0162..77e3dee1fef 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -245,13 +245,13 @@ impl<'a> MakeBcbCounters<'a> {
         // the loop. The `traversal` state includes a `context_stack`, providing a way to know if
         // the current BCB is in one or more nested loops or not.
         let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks);
-        while let Some(bcb) = traversal.next(self.basic_coverage_blocks) {
+        while let Some(bcb) = traversal.next() {
             if bcb_has_coverage_spans(bcb) {
                 debug!("{:?} has at least one coverage span. Get or make its counter", bcb);
                 let branching_counter_operand = self.get_or_make_counter_operand(bcb)?;
 
                 if self.bcb_needs_branch_counters(bcb) {
-                    self.make_branch_counters(&mut traversal, bcb, branching_counter_operand)?;
+                    self.make_branch_counters(&traversal, bcb, branching_counter_operand)?;
                 }
             } else {
                 debug!(
@@ -274,7 +274,7 @@ impl<'a> MakeBcbCounters<'a> {
 
     fn make_branch_counters(
         &mut self,
-        traversal: &mut TraverseCoverageGraphWithLoops,
+        traversal: &TraverseCoverageGraphWithLoops<'_>,
         branching_bcb: BasicCoverageBlock,
         branching_counter_operand: Operand,
     ) -> Result<(), Error> {
@@ -507,21 +507,14 @@ impl<'a> MakeBcbCounters<'a> {
     /// found, select any branch.
     fn choose_preferred_expression_branch(
         &self,
-        traversal: &TraverseCoverageGraphWithLoops,
+        traversal: &TraverseCoverageGraphWithLoops<'_>,
         branches: &[BcbBranch],
     ) -> BcbBranch {
-        let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
-
-        let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches);
-        if let Some(reloop_branch_without_counter) =
-            some_reloop_branch.filter(branch_needs_a_counter)
-        {
-            debug!(
-                "Selecting reloop_branch={:?} that still needs a counter, to get the \
-                `Expression`",
-                reloop_branch_without_counter
-            );
-            reloop_branch_without_counter
+        let good_reloop_branch = self.find_good_reloop_branch(traversal, &branches);
+        if let Some(reloop_branch) = good_reloop_branch {
+            assert!(self.branch_has_no_counter(&reloop_branch));
+            debug!("Selecting reloop branch {reloop_branch:?} to get an expression");
+            reloop_branch
         } else {
             let &branch_without_counter =
                 branches.iter().find(|&branch| self.branch_has_no_counter(branch)).expect(
@@ -538,75 +531,52 @@ impl<'a> MakeBcbCounters<'a> {
         }
     }
 
-    /// At most, one of the branches (or its edge, from the branching_bcb, if the branch has
-    /// multiple incoming edges) can have a counter computed by expression.
-    ///
-    /// If at least one of the branches leads outside of a loop (`found_loop_exit` is
-    /// true), and at least one other branch does not exit the loop (the first of which
-    /// is captured in `some_reloop_branch`), it's likely any reloop branch will be
-    /// executed far more often than loop exit branch, making the reloop branch a better
-    /// candidate for an expression.
-    fn find_some_reloop_branch(
+    /// Tries to find a branch that leads back to the top of a loop, and that
+    /// doesn't already have a counter. Such branches are good candidates to
+    /// be given an expression (instead of a physical counter), because they
+    /// will tend to be executed more times than a loop-exit branch.
+    fn find_good_reloop_branch(
         &self,
-        traversal: &TraverseCoverageGraphWithLoops,
+        traversal: &TraverseCoverageGraphWithLoops<'_>,
         branches: &[BcbBranch],
     ) -> Option<BcbBranch> {
-        let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
-
-        let mut some_reloop_branch: Option<BcbBranch> = None;
-        for context in traversal.context_stack.iter().rev() {
-            if let Some((backedge_from_bcbs, _)) = &context.loop_backedges {
-                let mut found_loop_exit = false;
-                for &branch in branches.iter() {
-                    if backedge_from_bcbs.iter().any(|&backedge_from_bcb| {
-                        self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
-                    }) {
-                        if let Some(reloop_branch) = some_reloop_branch {
-                            if self.branch_has_no_counter(&reloop_branch) {
-                                // we already found a candidate reloop_branch that still
-                                // needs a counter
-                                continue;
-                            }
-                        }
-                        // The path from branch leads back to the top of the loop. Set this
-                        // branch as the `reloop_branch`. If this branch already has a
-                        // counter, and we find another reloop branch that doesn't have a
-                        // counter yet, that branch will be selected as the `reloop_branch`
-                        // instead.
-                        some_reloop_branch = Some(branch);
-                    } else {
-                        // The path from branch leads outside this loop
-                        found_loop_exit = true;
-                    }
-                    if found_loop_exit
-                        && some_reloop_branch.filter(branch_needs_a_counter).is_some()
-                    {
-                        // Found both a branch that exits the loop and a branch that returns
-                        // to the top of the loop (`reloop_branch`), and the `reloop_branch`
-                        // doesn't already have a counter.
-                        break;
+        // Consider each loop on the current traversal context stack, top-down.
+        for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
+            let mut all_branches_exit_this_loop = true;
+
+            // Try to find a branch that doesn't exit this loop and doesn't
+            // already have a counter.
+            for &branch in branches {
+                // A branch is a reloop branch if it dominates any BCB that has
+                // an edge back to the loop header. (Other branches are exits.)
+                let is_reloop_branch = reloop_bcbs.iter().any(|&reloop_bcb| {
+                    self.basic_coverage_blocks.dominates(branch.target_bcb, reloop_bcb)
+                });
+
+                if is_reloop_branch {
+                    all_branches_exit_this_loop = false;
+                    if self.branch_has_no_counter(&branch) {
+                        // We found a good branch to be given an expression.
+                        return Some(branch);
                     }
+                    // Keep looking for another reloop branch without a counter.
+                } else {
+                    // This branch exits the loop.
                 }
-                if !found_loop_exit {
-                    debug!(
-                        "No branches exit the loop, so any branch without an existing \
-                        counter can have the `Expression`."
-                    );
-                    break;
-                }
-                if some_reloop_branch.is_some() {
-                    debug!(
-                        "Found a branch that exits the loop and a branch the loops back to \
-                        the top of the loop (`reloop_branch`). The `reloop_branch` will \
-                        get the `Expression`, as long as it still needs a counter."
-                    );
-                    break;
-                }
-                // else all branches exited this loop context, so run the same checks with
-                // the outer loop(s)
             }
+
+            if !all_branches_exit_this_loop {
+                // We found one or more reloop branches, but all of them already
+                // have counters. Let the caller choose one of the exit branches.
+                debug!("All reloop branches had counters; skip checking the other loops");
+                return None;
+            }
+
+            // All of the branches exit this loop, so keep looking for a good
+            // reloop branch for one of the outer loops.
         }
-        some_reloop_branch
+
+        None
     }
 
     #[inline]
@@ -652,9 +622,4 @@ impl<'a> MakeBcbCounters<'a> {
     fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool {
         self.bcb_predecessors(bcb).len() <= 1
     }
-
-    #[inline]
-    fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
-        self.basic_coverage_blocks.dominates(dom, node)
-    }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 812633348e3..9a7adaada09 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -1,10 +1,12 @@
+use rustc_data_structures::captures::Captures;
 use rustc_data_structures::graph::dominators::{self, Dominators};
 use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode};
 use rustc_index::bit_set::BitSet;
 use rustc_index::{IndexSlice, IndexVec};
-use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind};
+use rustc_middle::mir::{self, BasicBlock, TerminatorKind};
 
 use std::cmp::Ordering;
+use std::collections::VecDeque;
 use std::ops::{Index, IndexMut};
 
 /// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
@@ -36,9 +38,8 @@ impl CoverageGraph {
                 }
                 let bcb_data = &bcbs[bcb];
                 let mut bcb_successors = Vec::new();
-                for successor in
-                    bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind)
-                        .filter_map(|successor_bb| bb_to_bcb[successor_bb])
+                for successor in bcb_filtered_successors(&mir_body, bcb_data.last_bb())
+                    .filter_map(|successor_bb| bb_to_bcb[successor_bb])
                 {
                     if !seen[successor] {
                         seen[successor] = true;
@@ -80,10 +81,9 @@ impl CoverageGraph {
         // intentionally omits unwind paths.
         // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
         // `catch_unwind()` handlers.
-        let mir_cfg_without_unwind = ShortCircuitPreorder::new(&mir_body, bcb_filtered_successors);
 
         let mut basic_blocks = Vec::new();
-        for (bb, data) in mir_cfg_without_unwind {
+        for bb in short_circuit_preorder(mir_body, bcb_filtered_successors) {
             if let Some(last) = basic_blocks.last() {
                 let predecessors = &mir_body.basic_blocks.predecessors()[bb];
                 if predecessors.len() > 1 || !predecessors.contains(last) {
@@ -109,7 +109,7 @@ impl CoverageGraph {
             }
             basic_blocks.push(bb);
 
-            let term = data.terminator();
+            let term = mir_body[bb].terminator();
 
             match term.kind {
                 TerminatorKind::Return { .. }
@@ -316,11 +316,6 @@ impl BasicCoverageBlockData {
     pub fn last_bb(&self) -> BasicBlock {
         *self.basic_blocks.last().unwrap()
     }
-
-    #[inline(always)]
-    pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> {
-        &mir_body[self.last_bb()].terminator()
-    }
 }
 
 /// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`)
@@ -362,26 +357,28 @@ impl std::fmt::Debug for BcbBranch {
     }
 }
 
-// Returns the `Terminator`s non-unwind successors.
+// Returns the subset of a block's successors that are relevant to the coverage
+// graph, i.e. those that do not represent unwinds or unreachable branches.
 // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
 // `catch_unwind()` handlers.
 fn bcb_filtered_successors<'a, 'tcx>(
     body: &'a mir::Body<'tcx>,
-    term_kind: &'a TerminatorKind<'tcx>,
-) -> Box<dyn Iterator<Item = BasicBlock> + 'a> {
-    Box::new(
-        match &term_kind {
-            // SwitchInt successors are never unwind, and all of them should be traversed.
-            TerminatorKind::SwitchInt { ref targets, .. } => {
-                None.into_iter().chain(targets.all_targets().into_iter().copied())
-            }
-            // For all other kinds, return only the first successor, if any, and ignore unwinds.
-            // NOTE: `chain(&[])` is required to coerce the `option::iter` (from
-            // `next().into_iter()`) into the `mir::Successors` aliased type.
-            _ => term_kind.successors().next().into_iter().chain((&[]).into_iter().copied()),
-        }
-        .filter(move |&successor| body[successor].terminator().kind != TerminatorKind::Unreachable),
-    )
+    bb: BasicBlock,
+) -> impl Iterator<Item = BasicBlock> + Captures<'a> + Captures<'tcx> {
+    let terminator = body[bb].terminator();
+
+    let take_n_successors = match terminator.kind {
+        // SwitchInt successors are never unwinds, so all of them should be traversed.
+        TerminatorKind::SwitchInt { .. } => usize::MAX,
+        // For all other kinds, return only the first successor (if any), ignoring any
+        // unwind successors.
+        _ => 1,
+    };
+
+    terminator
+        .successors()
+        .take(take_n_successors)
+        .filter(move |&successor| body[successor].terminator().kind != TerminatorKind::Unreachable)
 }
 
 /// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the
@@ -389,57 +386,72 @@ fn bcb_filtered_successors<'a, 'tcx>(
 /// ensures a loop is completely traversed before processing Blocks after the end of the loop.
 #[derive(Debug)]
 pub(super) struct TraversalContext {
-    /// From one or more backedges returning to a loop header.
-    pub loop_backedges: Option<(Vec<BasicCoverageBlock>, BasicCoverageBlock)>,
-
-    /// worklist, to be traversed, of CoverageGraph in the loop with the given loop
-    /// backedges, such that the loop is the inner inner-most loop containing these
-    /// CoverageGraph
-    pub worklist: Vec<BasicCoverageBlock>,
+    /// BCB with one or more incoming loop backedges, indicating which loop
+    /// this context is for.
+    ///
+    /// If `None`, this is the non-loop context for the function as a whole.
+    loop_header: Option<BasicCoverageBlock>,
+
+    /// Worklist of BCBs to be processed in this context.
+    worklist: VecDeque<BasicCoverageBlock>,
 }
 
-pub(super) struct TraverseCoverageGraphWithLoops {
-    pub backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
-    pub context_stack: Vec<TraversalContext>,
+pub(super) struct TraverseCoverageGraphWithLoops<'a> {
+    basic_coverage_blocks: &'a CoverageGraph,
+
+    backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
+    context_stack: Vec<TraversalContext>,
     visited: BitSet<BasicCoverageBlock>,
 }
 
-impl TraverseCoverageGraphWithLoops {
-    pub fn new(basic_coverage_blocks: &CoverageGraph) -> Self {
-        let start_bcb = basic_coverage_blocks.start_node();
+impl<'a> TraverseCoverageGraphWithLoops<'a> {
+    pub(super) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self {
         let backedges = find_loop_backedges(basic_coverage_blocks);
-        let context_stack =
-            vec![TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }];
+
+        let worklist = VecDeque::from([basic_coverage_blocks.start_node()]);
+        let context_stack = vec![TraversalContext { loop_header: None, worklist }];
+
         // `context_stack` starts with a `TraversalContext` for the main function context (beginning
         // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
         // of the stack as loops are entered, and popped off of the stack when a loop's worklist is
         // exhausted.
         let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes());
-        Self { backedges, context_stack, visited }
+        Self { basic_coverage_blocks, backedges, context_stack, visited }
     }
 
-    pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option<BasicCoverageBlock> {
+    /// For each loop on the loop context stack (top-down), yields a list of BCBs
+    /// within that loop that have an outgoing edge back to the loop header.
+    pub(super) fn reloop_bcbs_per_loop(&self) -> impl Iterator<Item = &[BasicCoverageBlock]> {
+        self.context_stack
+            .iter()
+            .rev()
+            .filter_map(|context| context.loop_header)
+            .map(|header_bcb| self.backedges[header_bcb].as_slice())
+    }
+
+    pub(super) fn next(&mut self) -> Option<BasicCoverageBlock> {
         debug!(
             "TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
             self.context_stack.iter().rev().collect::<Vec<_>>()
         );
 
         while let Some(context) = self.context_stack.last_mut() {
-            if let Some(next_bcb) = context.worklist.pop() {
-                if !self.visited.insert(next_bcb) {
-                    debug!("Already visited: {:?}", next_bcb);
+            if let Some(bcb) = context.worklist.pop_front() {
+                if !self.visited.insert(bcb) {
+                    debug!("Already visited: {bcb:?}");
                     continue;
                 }
-                debug!("Visiting {:?}", next_bcb);
-                if self.backedges[next_bcb].len() > 0 {
-                    debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb);
+                debug!("Visiting {bcb:?}");
+
+                if self.backedges[bcb].len() > 0 {
+                    debug!("{bcb:?} is a loop header! Start a new TraversalContext...");
                     self.context_stack.push(TraversalContext {
-                        loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)),
-                        worklist: Vec::new(),
+                        loop_header: Some(bcb),
+                        worklist: VecDeque::new(),
                     });
                 }
-                self.extend_worklist(basic_coverage_blocks, next_bcb);
-                return Some(next_bcb);
+                self.add_successors_to_worklists(bcb);
+                return Some(bcb);
             } else {
                 // Strip contexts with empty worklists from the top of the stack
                 self.context_stack.pop();
@@ -449,13 +461,10 @@ impl TraverseCoverageGraphWithLoops {
         None
     }
 
-    pub fn extend_worklist(
-        &mut self,
-        basic_coverage_blocks: &CoverageGraph,
-        bcb: BasicCoverageBlock,
-    ) {
-        let successors = &basic_coverage_blocks.successors[bcb];
+    pub fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) {
+        let successors = &self.basic_coverage_blocks.successors[bcb];
         debug!("{:?} has {} successors:", bcb, successors.len());
+
         for &successor in successors {
             if successor == bcb {
                 debug!(
@@ -464,56 +473,44 @@ impl TraverseCoverageGraphWithLoops {
                     bcb
                 );
                 // Don't re-add this successor to the worklist. We are already processing it.
+                // FIXME: This claims to skip just the self-successor, but it actually skips
+                // all other successors as well. Does that matter?
                 break;
             }
-            for context in self.context_stack.iter_mut().rev() {
-                // Add successors of the current BCB to the appropriate context. Successors that
-                // stay within a loop are added to the BCBs context worklist. Successors that
-                // exit the loop (they are not dominated by the loop header) must be reachable
-                // from other BCBs outside the loop, and they will be added to a different
-                // worklist.
-                //
-                // Branching blocks (with more than one successor) must be processed before
-                // blocks with only one successor, to prevent unnecessarily complicating
-                // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
-                // branching block would have given an `Expression` (or vice versa).
-                let (some_successor_to_add, some_loop_header) =
-                    if let Some((_, loop_header)) = context.loop_backedges {
-                        if basic_coverage_blocks.dominates(loop_header, successor) {
-                            (Some(successor), Some(loop_header))
-                        } else {
-                            (None, None)
-                        }
-                    } else {
-                        (Some(successor), None)
-                    };
-                if let Some(successor_to_add) = some_successor_to_add {
-                    if basic_coverage_blocks.successors[successor_to_add].len() > 1 {
-                        debug!(
-                            "{:?} successor is branching. Prioritize it at the beginning of \
-                            the {}",
-                            successor_to_add,
-                            if let Some(loop_header) = some_loop_header {
-                                format!("worklist for the loop headed by {loop_header:?}")
-                            } else {
-                                String::from("non-loop worklist")
-                            },
-                        );
-                        context.worklist.insert(0, successor_to_add);
-                    } else {
-                        debug!(
-                            "{:?} successor is non-branching. Defer it to the end of the {}",
-                            successor_to_add,
-                            if let Some(loop_header) = some_loop_header {
-                                format!("worklist for the loop headed by {loop_header:?}")
-                            } else {
-                                String::from("non-loop worklist")
-                            },
-                        );
-                        context.worklist.push(successor_to_add);
+
+            // Add successors of the current BCB to the appropriate context. Successors that
+            // stay within a loop are added to the BCBs context worklist. Successors that
+            // exit the loop (they are not dominated by the loop header) must be reachable
+            // from other BCBs outside the loop, and they will be added to a different
+            // worklist.
+            //
+            // Branching blocks (with more than one successor) must be processed before
+            // blocks with only one successor, to prevent unnecessarily complicating
+            // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
+            // branching block would have given an `Expression` (or vice versa).
+
+            let context = self
+                .context_stack
+                .iter_mut()
+                .rev()
+                .find(|context| match context.loop_header {
+                    Some(loop_header) => {
+                        self.basic_coverage_blocks.dominates(loop_header, successor)
                     }
-                    break;
-                }
+                    None => true,
+                })
+                .unwrap_or_else(|| bug!("should always fall back to the root non-loop context"));
+            debug!("adding to worklist for {:?}", context.loop_header);
+
+            // FIXME: The code below had debug messages claiming to add items to a
+            // particular end of the worklist, but was confused about which end was
+            // which. The existing behaviour has been preserved for now, but it's
+            // unclear what the intended behaviour was.
+
+            if self.basic_coverage_blocks.successors[successor].len() > 1 {
+                context.worklist.push_back(successor);
+            } else {
+                context.worklist.push_front(successor);
             }
         }
     }
@@ -553,66 +550,28 @@ pub(super) fn find_loop_backedges(
     backedges
 }
 
-pub struct ShortCircuitPreorder<
-    'a,
-    'tcx,
-    F: Fn(&'a mir::Body<'tcx>, &'a TerminatorKind<'tcx>) -> Box<dyn Iterator<Item = BasicBlock> + 'a>,
-> {
+fn short_circuit_preorder<'a, 'tcx, F, Iter>(
     body: &'a mir::Body<'tcx>,
-    visited: BitSet<BasicBlock>,
-    worklist: Vec<BasicBlock>,
     filtered_successors: F,
-}
-
-impl<
-    'a,
-    'tcx,
-    F: Fn(&'a mir::Body<'tcx>, &'a TerminatorKind<'tcx>) -> Box<dyn Iterator<Item = BasicBlock> + 'a>,
-> ShortCircuitPreorder<'a, 'tcx, F>
-{
-    pub fn new(
-        body: &'a mir::Body<'tcx>,
-        filtered_successors: F,
-    ) -> ShortCircuitPreorder<'a, 'tcx, F> {
-        let worklist = vec![mir::START_BLOCK];
-
-        ShortCircuitPreorder {
-            body,
-            visited: BitSet::new_empty(body.basic_blocks.len()),
-            worklist,
-            filtered_successors,
-        }
-    }
-}
-
-impl<
-    'a,
-    'tcx,
-    F: Fn(&'a mir::Body<'tcx>, &'a TerminatorKind<'tcx>) -> Box<dyn Iterator<Item = BasicBlock> + 'a>,
-> Iterator for ShortCircuitPreorder<'a, 'tcx, F>
+) -> impl Iterator<Item = BasicBlock> + Captures<'a> + Captures<'tcx>
+where
+    F: Fn(&'a mir::Body<'tcx>, BasicBlock) -> Iter,
+    Iter: Iterator<Item = BasicBlock>,
 {
-    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+    let mut visited = BitSet::new_empty(body.basic_blocks.len());
+    let mut worklist = vec![mir::START_BLOCK];
 
-    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
-        while let Some(idx) = self.worklist.pop() {
-            if !self.visited.insert(idx) {
+    std::iter::from_fn(move || {
+        while let Some(bb) = worklist.pop() {
+            if !visited.insert(bb) {
                 continue;
             }
 
-            let data = &self.body[idx];
-
-            if let Some(ref term) = data.terminator {
-                self.worklist.extend((self.filtered_successors)(&self.body, &term.kind));
-            }
+            worklist.extend(filtered_successors(body, bb));
 
-            return Some((idx, data));
+            return Some(bb);
         }
 
         None
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let size = self.body.basic_blocks.len() - self.visited.count();
-        (size, Some(size))
-    }
+    })
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index dd87694f97c..e08019bea21 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,15 +1,13 @@
-use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB};
+use std::cell::OnceCell;
 
 use rustc_data_structures::graph::WithNumNodes;
 use rustc_index::IndexVec;
-use rustc_middle::mir::{
-    self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
-    TerminatorKind,
-};
-use rustc_span::source_map::original_sp;
+use rustc_middle::mir::{self, AggregateKind, Rvalue, Statement, StatementKind};
 use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
 
-use std::cell::OnceCell;
+use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
+
+mod from_mir;
 
 pub(super) struct CoverageSpans {
     /// Map from BCBs to their list of coverage spans.
@@ -53,27 +51,13 @@ impl CoverageSpans {
     }
 }
 
-#[derive(Debug, Copy, Clone)]
-pub(super) enum CoverageStatement {
-    Statement(BasicBlock, Span, usize),
-    Terminator(BasicBlock, Span),
-}
-
-impl CoverageStatement {
-    pub fn span(&self) -> Span {
-        match self {
-            Self::Statement(_, span, _) | Self::Terminator(_, span) => *span,
-        }
-    }
-}
-
 /// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that
 /// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s.
 /// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent
 /// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the
-/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s.
+/// `merged_spans` vectors, and the `Span`s to cover the extent of the combined `Span`s.
 ///
-/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that
+/// Note: A span merged into another CoverageSpan may come from a `BasicBlock` that
 /// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches
 /// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock`
 /// `dominates()` the `BasicBlock`s in this `CoverageSpan`.
@@ -83,7 +67,9 @@ struct CoverageSpan {
     pub expn_span: Span,
     pub current_macro_or_none: OnceCell<Option<Symbol>>,
     pub bcb: BasicCoverageBlock,
-    pub coverage_statements: Vec<CoverageStatement>,
+    /// List of all the original spans from MIR that have been merged into this
+    /// span. Mainly used to precisely skip over gaps when truncating a span.
+    pub merged_spans: Vec<Span>,
     pub is_closure: bool,
 }
 
@@ -94,7 +80,7 @@ impl CoverageSpan {
             expn_span: fn_sig_span,
             current_macro_or_none: Default::default(),
             bcb: START_BCB,
-            coverage_statements: vec![],
+            merged_spans: vec![],
             is_closure: false,
         }
     }
@@ -104,8 +90,6 @@ impl CoverageSpan {
         span: Span,
         expn_span: Span,
         bcb: BasicCoverageBlock,
-        bb: BasicBlock,
-        stmt_index: usize,
     ) -> Self {
         let is_closure = match statement.kind {
             StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => {
@@ -119,23 +103,18 @@ impl CoverageSpan {
             expn_span,
             current_macro_or_none: Default::default(),
             bcb,
-            coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
+            merged_spans: vec![span],
             is_closure,
         }
     }
 
-    pub fn for_terminator(
-        span: Span,
-        expn_span: Span,
-        bcb: BasicCoverageBlock,
-        bb: BasicBlock,
-    ) -> Self {
+    pub fn for_terminator(span: Span, expn_span: Span, bcb: BasicCoverageBlock) -> Self {
         Self {
             span,
             expn_span,
             current_macro_or_none: Default::default(),
             bcb,
-            coverage_statements: vec![CoverageStatement::Terminator(bb, span)],
+            merged_spans: vec![span],
             is_closure: false,
         }
     }
@@ -143,15 +122,13 @@ impl CoverageSpan {
     pub fn merge_from(&mut self, mut other: CoverageSpan) {
         debug_assert!(self.is_mergeable(&other));
         self.span = self.span.to(other.span);
-        self.coverage_statements.append(&mut other.coverage_statements);
+        self.merged_spans.append(&mut other.merged_spans);
     }
 
     pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
-        self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos);
-        if let Some(highest_covstmt) =
-            self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi())
-        {
-            self.span = self.span.with_hi(highest_covstmt.span().hi());
+        self.merged_spans.retain(|span| span.hi() <= cutoff_pos);
+        if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() {
+            self.span = self.span.with_hi(max_hi);
         }
     }
 
@@ -205,13 +182,7 @@ impl CoverageSpan {
 ///  * Merge spans that represent continuous (both in source code and control flow), non-branching
 ///    execution
 ///  * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
-struct CoverageSpansGenerator<'a, 'tcx> {
-    /// The MIR, used to look up `BasicBlockData`.
-    mir_body: &'a mir::Body<'tcx>,
-
-    /// A `Span` covering the signature of function for the MIR.
-    fn_sig_span: Span,
-
+struct CoverageSpansGenerator<'a> {
     /// A `Span` covering the function body of the MIR (typically from left curly brace to right
     /// curly brace).
     body_span: Span,
@@ -221,7 +192,7 @@ struct CoverageSpansGenerator<'a, 'tcx> {
 
     /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative
     /// dominance between the `BasicCoverageBlock`s of equal `Span`s.
-    sorted_spans_iter: Option<std::vec::IntoIter<CoverageSpan>>,
+    sorted_spans_iter: std::vec::IntoIter<CoverageSpan>,
 
     /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the
     /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to
@@ -261,7 +232,7 @@ struct CoverageSpansGenerator<'a, 'tcx> {
     refined_spans: Vec<CoverageSpan>,
 }
 
-impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
+impl<'a> CoverageSpansGenerator<'a> {
     /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be
     /// counted.
     ///
@@ -284,17 +255,22 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
     /// Note the resulting vector of `CoverageSpan`s may not be fully sorted (and does not need
     /// to be).
     pub(super) fn generate_coverage_spans(
-        mir_body: &'a mir::Body<'tcx>,
+        mir_body: &mir::Body<'_>,
         fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span`
         body_span: Span,
         basic_coverage_blocks: &'a CoverageGraph,
     ) -> Vec<CoverageSpan> {
-        let mut coverage_spans = Self {
+        let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans(
             mir_body,
             fn_sig_span,
             body_span,
             basic_coverage_blocks,
-            sorted_spans_iter: None,
+        );
+
+        let coverage_spans = Self {
+            body_span,
+            basic_coverage_blocks,
+            sorted_spans_iter: sorted_spans.into_iter(),
             refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2),
             some_curr: None,
             curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
@@ -304,48 +280,9 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
             pending_dups: Vec::new(),
         };
 
-        let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans();
-
-        coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter());
-
         coverage_spans.to_refined_spans()
     }
 
-    fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> {
-        let mut initial_spans =
-            Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks.len() * 2);
-        for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() {
-            initial_spans.extend(self.bcb_to_initial_coverage_spans(bcb, bcb_data));
-        }
-
-        if initial_spans.is_empty() {
-            // This can happen if, for example, the function is unreachable (contains only a
-            // `BasicBlock`(s) with an `Unreachable` terminator).
-            return initial_spans;
-        }
-
-        initial_spans.push(CoverageSpan::for_fn_sig(self.fn_sig_span));
-
-        initial_spans.sort_by(|a, b| {
-            // First sort by span start.
-            Ord::cmp(&a.span.lo(), &b.span.lo())
-                // If span starts are the same, sort by span end in reverse order.
-                // This ensures that if spans A and B are adjacent in the list,
-                // and they overlap but are not equal, then either:
-                // - Span A extends further left, or
-                // - Both have the same start and span A extends further right
-                .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
-                // If both spans are equal, sort the BCBs in dominator order,
-                // so that dominating BCBs come before other BCBs they dominate.
-                .then_with(|| self.basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
-                // If two spans are otherwise identical, put closure spans first,
-                // as this seems to be what the refinement step expects.
-                .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
-        });
-
-        initial_spans
-    }
-
     /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and
     /// de-duplicated `CoverageSpan`s.
     fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
@@ -485,48 +422,6 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
         }
     }
 
-    // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
-    // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
-    // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
-    // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple
-    // `Statement`s and/or `Terminator`s.)
-    fn bcb_to_initial_coverage_spans(
-        &self,
-        bcb: BasicCoverageBlock,
-        bcb_data: &'a BasicCoverageBlockData,
-    ) -> Vec<CoverageSpan> {
-        bcb_data
-            .basic_blocks
-            .iter()
-            .flat_map(|&bb| {
-                let data = &self.mir_body[bb];
-                data.statements
-                    .iter()
-                    .enumerate()
-                    .filter_map(move |(index, statement)| {
-                        filtered_statement_span(statement).map(|span| {
-                            CoverageSpan::for_statement(
-                                statement,
-                                function_source_span(span, self.body_span),
-                                span,
-                                bcb,
-                                bb,
-                                index,
-                            )
-                        })
-                    })
-                    .chain(filtered_terminator_span(data.terminator()).map(|span| {
-                        CoverageSpan::for_terminator(
-                            function_source_span(span, self.body_span),
-                            span,
-                            bcb,
-                            bb,
-                        )
-                    }))
-            })
-            .collect()
-    }
-
     fn curr(&self) -> &CoverageSpan {
         self.some_curr
             .as_ref()
@@ -589,7 +484,7 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
             self.some_prev = Some(curr);
             self.prev_original_span = self.curr_original_span;
         }
-        while let Some(curr) = self.sorted_spans_iter.as_mut().unwrap().next() {
+        while let Some(curr) = self.sorted_spans_iter.next() {
             debug!("FOR curr={:?}", curr);
             if self.some_prev.is_some() && self.prev_starts_after_next(&curr) {
                 debug!(
@@ -757,7 +652,7 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
         if self.pending_dups.is_empty() {
             let curr_span = self.curr().span;
             self.prev_mut().cutoff_statements_at(curr_span.lo());
-            if self.prev().coverage_statements.is_empty() {
+            if self.prev().merged_spans.is_empty() {
                 debug!("  ... no non-overlapping statements to add");
             } else {
                 debug!("  ... adding modified prev={:?}", self.prev());
@@ -774,104 +669,3 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
         self.basic_coverage_blocks.dominates(dom_covspan.bcb, covspan.bcb)
     }
 }
-
-/// If the MIR `Statement` has a span contributive to computing coverage spans,
-/// return it; otherwise return `None`.
-fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
-    match statement.kind {
-        // These statements have spans that are often outside the scope of the executed source code
-        // for their parent `BasicBlock`.
-        StatementKind::StorageLive(_)
-        | StatementKind::StorageDead(_)
-        // Coverage should not be encountered, but don't inject coverage coverage
-        | StatementKind::Coverage(_)
-        // Ignore `ConstEvalCounter`s
-        | StatementKind::ConstEvalCounter
-        // Ignore `Nop`s
-        | StatementKind::Nop => None,
-
-        // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead`
-        // statements be more consistent?
-        //
-        // FakeReadCause::ForGuardBinding, in this example:
-        //     match somenum {
-        //         x if x < 1 => { ... }
-        //     }...
-        // The BasicBlock within the match arm code included one of these statements, but the span
-        // for it covered the `1` in this source. The actual statements have nothing to do with that
-        // source span:
-        //     FakeRead(ForGuardBinding, _4);
-        // where `_4` is:
-        //     _4 = &_1; (at the span for the first `x`)
-        // and `_1` is the `Place` for `somenum`.
-        //
-        // If and when the Issue is resolved, remove this special case match pattern:
-        StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
-
-        // Retain spans from all other statements
-        StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
-        | StatementKind::Intrinsic(..)
-        | StatementKind::Assign(_)
-        | StatementKind::SetDiscriminant { .. }
-        | StatementKind::Deinit(..)
-        | StatementKind::Retag(_, _)
-        | StatementKind::PlaceMention(..)
-        | StatementKind::AscribeUserType(_, _) => {
-            Some(statement.source_info.span)
-        }
-    }
-}
-
-/// If the MIR `Terminator` has a span contributive to computing coverage spans,
-/// return it; otherwise return `None`.
-fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
-    match terminator.kind {
-        // These terminators have spans that don't positively contribute to computing a reasonable
-        // span of actually executed source code. (For example, SwitchInt terminators extracted from
-        // an `if condition { block }` has a span that includes the executed block, if true,
-        // but for coverage, the code region executed, up to *and* through the SwitchInt,
-        // actually stops before the if's block.)
-        TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
-        | TerminatorKind::Assert { .. }
-        | TerminatorKind::Drop { .. }
-        | TerminatorKind::SwitchInt { .. }
-        // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
-        | TerminatorKind::FalseEdge { .. }
-        | TerminatorKind::Goto { .. } => None,
-
-        // Call `func` operand can have a more specific span when part of a chain of calls
-        | TerminatorKind::Call { ref func, .. } => {
-            let mut span = terminator.source_info.span;
-            if let mir::Operand::Constant(box constant) = func {
-                if constant.span.lo() > span.lo() {
-                    span = span.with_lo(constant.span.lo());
-                }
-            }
-            Some(span)
-        }
-
-        // Retain spans from all other terminators
-        TerminatorKind::UnwindResume
-        | TerminatorKind::UnwindTerminate(_)
-        | TerminatorKind::Return
-        | TerminatorKind::Yield { .. }
-        | TerminatorKind::GeneratorDrop
-        | TerminatorKind::FalseUnwind { .. }
-        | TerminatorKind::InlineAsm { .. } => {
-            Some(terminator.source_info.span)
-        }
-    }
-}
-
-/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
-/// within the function's body source. This span is guaranteed to be contained
-/// within, or equal to, the `body_span`. If the extrapolated span is not
-/// contained within the `body_span`, the `body_span` is returned.
-///
-/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
-/// etc.).
-#[inline]
-fn function_source_span(span: Span, body_span: Span) -> Span {
-    let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
-    if body_span.contains(original_span) { original_span } else { body_span }
-}
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
new file mode 100644
index 00000000000..4c20997e633
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -0,0 +1,184 @@
+use rustc_middle::mir::{
+    self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
+};
+use rustc_span::Span;
+
+use crate::coverage::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
+use crate::coverage::spans::CoverageSpan;
+
+pub(super) fn mir_to_initial_sorted_coverage_spans(
+    mir_body: &mir::Body<'_>,
+    fn_sig_span: Span,
+    body_span: Span,
+    basic_coverage_blocks: &CoverageGraph,
+) -> Vec<CoverageSpan> {
+    let mut initial_spans = Vec::<CoverageSpan>::with_capacity(mir_body.basic_blocks.len() * 2);
+    for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
+        initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data));
+    }
+
+    if initial_spans.is_empty() {
+        // This can happen if, for example, the function is unreachable (contains only a
+        // `BasicBlock`(s) with an `Unreachable` terminator).
+        return initial_spans;
+    }
+
+    initial_spans.push(CoverageSpan::for_fn_sig(fn_sig_span));
+
+    initial_spans.sort_by(|a, b| {
+        // First sort by span start.
+        Ord::cmp(&a.span.lo(), &b.span.lo())
+            // If span starts are the same, sort by span end in reverse order.
+            // This ensures that if spans A and B are adjacent in the list,
+            // and they overlap but are not equal, then either:
+            // - Span A extends further left, or
+            // - Both have the same start and span A extends further right
+            .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
+            // If both spans are equal, sort the BCBs in dominator order,
+            // so that dominating BCBs come before other BCBs they dominate.
+            .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
+            // If two spans are otherwise identical, put closure spans first,
+            // as this seems to be what the refinement step expects.
+            .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
+    });
+
+    initial_spans
+}
+
+// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
+// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
+// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
+// merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple
+// `Statement`s and/or `Terminator`s.)
+fn bcb_to_initial_coverage_spans(
+    mir_body: &mir::Body<'_>,
+    body_span: Span,
+    bcb: BasicCoverageBlock,
+    bcb_data: &BasicCoverageBlockData,
+) -> Vec<CoverageSpan> {
+    bcb_data
+        .basic_blocks
+        .iter()
+        .flat_map(|&bb| {
+            let data = &mir_body[bb];
+            data.statements
+                .iter()
+                .filter_map(move |statement| {
+                    filtered_statement_span(statement).map(|span| {
+                        CoverageSpan::for_statement(
+                            statement,
+                            function_source_span(span, body_span),
+                            span,
+                            bcb,
+                        )
+                    })
+                })
+                .chain(filtered_terminator_span(data.terminator()).map(|span| {
+                    CoverageSpan::for_terminator(function_source_span(span, body_span), span, bcb)
+                }))
+        })
+        .collect()
+}
+
+/// If the MIR `Statement` has a span contributive to computing coverage spans,
+/// return it; otherwise return `None`.
+fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
+    match statement.kind {
+        // These statements have spans that are often outside the scope of the executed source code
+        // for their parent `BasicBlock`.
+        StatementKind::StorageLive(_)
+        | StatementKind::StorageDead(_)
+        // Coverage should not be encountered, but don't inject coverage coverage
+        | StatementKind::Coverage(_)
+        // Ignore `ConstEvalCounter`s
+        | StatementKind::ConstEvalCounter
+        // Ignore `Nop`s
+        | StatementKind::Nop => None,
+
+        // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead`
+        // statements be more consistent?
+        //
+        // FakeReadCause::ForGuardBinding, in this example:
+        //     match somenum {
+        //         x if x < 1 => { ... }
+        //     }...
+        // The BasicBlock within the match arm code included one of these statements, but the span
+        // for it covered the `1` in this source. The actual statements have nothing to do with that
+        // source span:
+        //     FakeRead(ForGuardBinding, _4);
+        // where `_4` is:
+        //     _4 = &_1; (at the span for the first `x`)
+        // and `_1` is the `Place` for `somenum`.
+        //
+        // If and when the Issue is resolved, remove this special case match pattern:
+        StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
+
+        // Retain spans from all other statements
+        StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
+        | StatementKind::Intrinsic(..)
+        | StatementKind::Assign(_)
+        | StatementKind::SetDiscriminant { .. }
+        | StatementKind::Deinit(..)
+        | StatementKind::Retag(_, _)
+        | StatementKind::PlaceMention(..)
+        | StatementKind::AscribeUserType(_, _) => {
+            Some(statement.source_info.span)
+        }
+    }
+}
+
+/// If the MIR `Terminator` has a span contributive to computing coverage spans,
+/// return it; otherwise return `None`.
+fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
+    match terminator.kind {
+        // These terminators have spans that don't positively contribute to computing a reasonable
+        // span of actually executed source code. (For example, SwitchInt terminators extracted from
+        // an `if condition { block }` has a span that includes the executed block, if true,
+        // but for coverage, the code region executed, up to *and* through the SwitchInt,
+        // actually stops before the if's block.)
+        TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
+        | TerminatorKind::Assert { .. }
+        | TerminatorKind::Drop { .. }
+        | TerminatorKind::SwitchInt { .. }
+        // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
+        | TerminatorKind::FalseEdge { .. }
+        | TerminatorKind::Goto { .. } => None,
+
+        // Call `func` operand can have a more specific span when part of a chain of calls
+        | TerminatorKind::Call { ref func, .. } => {
+            let mut span = terminator.source_info.span;
+            if let mir::Operand::Constant(box constant) = func {
+                if constant.span.lo() > span.lo() {
+                    span = span.with_lo(constant.span.lo());
+                }
+            }
+            Some(span)
+        }
+
+        // Retain spans from all other terminators
+        TerminatorKind::UnwindResume
+        | TerminatorKind::UnwindTerminate(_)
+        | TerminatorKind::Return
+        | TerminatorKind::Yield { .. }
+        | TerminatorKind::GeneratorDrop
+        | TerminatorKind::FalseUnwind { .. }
+        | TerminatorKind::InlineAsm { .. } => {
+            Some(terminator.source_info.span)
+        }
+    }
+}
+
+/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
+/// within the function's body source. This span is guaranteed to be contained
+/// within, or equal to, the `body_span`. If the extrapolated span is not
+/// contained within the `body_span`, the `body_span` is returned.
+///
+/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
+/// etc.).
+#[inline]
+fn function_source_span(span: Span, body_span: Span) -> Span {
+    use rustc_span::source_map::original_sp;
+
+    let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
+    if body_span.contains(original_span) { original_span } else { body_span }
+}
diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs
index 7476d3ce927..487d2282364 100644
--- a/compiler/rustc_mir_transform/src/coverage/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/tests.rs
@@ -241,7 +241,7 @@ fn print_coverage_graphviz(
                         "    {:?} [label=\"{:?}: {}\"];\n{}",
                         bcb,
                         bcb,
-                        bcb_data.terminator(mir_body).kind.name(),
+                        mir_body[bcb_data.last_bb()].terminator().kind.name(),
                         basic_coverage_blocks
                             .successors(bcb)
                             .map(|successor| { format!("    {:?} -> {:?};", bcb, successor) })
@@ -628,7 +628,7 @@ fn test_traverse_coverage_with_loops() {
     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
     let mut traversed_in_order = Vec::new();
     let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks);
-    while let Some(bcb) = traversal.next(&basic_coverage_blocks) {
+    while let Some(bcb) = traversal.next() {
         traversed_in_order.push(bcb);
     }
 
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 7b14fef6153..85a0be8a44c 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -2,7 +2,6 @@
 //!
 //! Currently, this pass only propagates scalar values.
 
-use rustc_const_eval::const_eval::CheckAlignment;
 use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
@@ -17,7 +16,7 @@ use rustc_mir_dataflow::value_analysis::{
 use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
 use rustc_span::def_id::DefId;
 use rustc_span::DUMMY_SP;
-use rustc_target::abi::{Align, FieldIdx, VariantIdx};
+use rustc_target::abi::{FieldIdx, VariantIdx};
 
 use crate::MirPass;
 
@@ -709,23 +708,13 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
     const PANIC_ON_ALLOC_FAIL: bool = true;
 
     #[inline(always)]
-    fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
-        // We do not check for alignment to avoid having to carry an `Align`
-        // in `ConstValue::ByRef`.
-        CheckAlignment::No
+    fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        false // no reason to enforce alignment
     }
 
     fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
         unimplemented!()
     }
-    fn alignment_check_failed(
-        _ecx: &InterpCx<'mir, 'tcx, Self>,
-        _has: Align,
-        _required: Align,
-        _check: CheckAlignment,
-    ) -> interpret::InterpResult<'tcx, ()> {
-        unimplemented!()
-    }
 
     fn before_access_global(
         _tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 319fb4eaf3e..6eb6cb069fe 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -95,6 +95,7 @@ pub struct EarlyOtherwiseBranch;
 
 impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
+        // unsound: https://github.com/rust-lang/rust/issues/95162
         sess.mir_opt_level() >= 3 && sess.opts.unstable_opts.unsound_mir_opts
     }
 
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 56bdc5a171a..c7529b954fe 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -63,7 +63,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_target::abi::{VariantIdx, FIRST_VARIANT};
 
-use crate::ssa::SsaLocals;
+use crate::ssa::{AssignedValue, SsaLocals};
 use crate::MirPass;
 
 pub struct GVN;
@@ -87,21 +87,28 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     let dominators = body.basic_blocks.dominators().clone();
 
     let mut state = VnState::new(tcx, param_env, &ssa, &dominators, &body.local_decls);
-    for arg in body.args_iter() {
-        if ssa.is_ssa(arg) {
-            let value = state.new_opaque().unwrap();
-            state.assign(arg, value);
-        }
-    }
-
-    ssa.for_each_assignment_mut(&mut body.basic_blocks, |local, rvalue, location| {
-        let value = state.simplify_rvalue(rvalue, location).or_else(|| state.new_opaque()).unwrap();
-        // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark `local` as
-        // reusable if we have an exact type match.
-        if state.local_decls[local].ty == rvalue.ty(state.local_decls, tcx) {
+    ssa.for_each_assignment_mut(
+        body.basic_blocks.as_mut_preserves_cfg(),
+        |local, value, location| {
+            let value = match value {
+                // We do not know anything of this assigned value.
+                AssignedValue::Arg | AssignedValue::Terminator(_) => None,
+                // Try to get some insight.
+                AssignedValue::Rvalue(rvalue) => {
+                    let value = state.simplify_rvalue(rvalue, location);
+                    // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark `local` as
+                    // reusable if we have an exact type match.
+                    if state.local_decls[local].ty != rvalue.ty(state.local_decls, tcx) {
+                        return;
+                    }
+                    value
+                }
+            };
+            // `next_opaque` is `Some`, so `new_opaque` must return `Some`.
+            let value = value.or_else(|| state.new_opaque()).unwrap();
             state.assign(local, value);
-        }
-    });
+        },
+    );
 
     // Stop creating opaques during replacement as it is useless.
     state.next_opaque = None;
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 886ff760481..0a8b13d6677 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -30,6 +30,9 @@ pub struct EnumSizeOpt {
 
 impl<'tcx> MirPass<'tcx> for EnumSizeOpt {
     fn is_enabled(&self, sess: &Session) -> bool {
+        // There are some differences in behavior on wasm and ARM that are not properly
+        // understood, so we conservatively treat this optimization as unsound:
+        // https://github.com/rust-lang/rust/pull/85158#issuecomment-1101836457
         sess.opts.unstable_opts.unsound_mir_opts || sess.mir_opt_level() >= 3
     }
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index c42888d8791..9ee2f6dcea0 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -496,7 +496,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         &elaborate_box_derefs::ElaborateBoxDerefs,
         &generator::StateTransform,
         &add_retag::AddRetag,
-        &Lint(const_prop_lint::ConstProp),
+        &Lint(const_prop_lint::ConstPropLint),
     ];
     pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial)));
 }
@@ -554,8 +554,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             &const_prop::ConstProp,
             &gvn::GVN,
             &dataflow_const_prop::DataflowConstProp,
-            //
-            // Const-prop runs unconditionally, but doesn't mutate the MIR at mir-opt-level=0.
             &const_debuginfo::ConstDebugInfo,
             &o1(simplify_branches::SimplifyConstCondition::AfterConstProp),
             &early_otherwise_branch::EarlyOtherwiseBranch,
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 0d2d764c422..22f9c6f4f85 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -2,9 +2,8 @@
 
 use crate::MirPass;
 use rustc_middle::mir::*;
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::symbol::sym;
 use rustc_target::abi::{FieldIdx, VariantIdx};
 
 pub struct LowerIntrinsics;
@@ -16,12 +15,10 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
             let terminator = block.terminator.as_mut().unwrap();
             if let TerminatorKind::Call { func, args, destination, target, .. } =
                 &mut terminator.kind
+                && let ty::FnDef(def_id, generic_args) = *func.ty(local_decls, tcx).kind()
+                && tcx.is_intrinsic(def_id)
             {
-                let func_ty = func.ty(local_decls, tcx);
-                let Some((intrinsic_name, generic_args)) = resolve_rust_intrinsic(tcx, func_ty)
-                else {
-                    continue;
-                };
+                let intrinsic_name = tcx.item_name(def_id);
                 match intrinsic_name {
                     sym::unreachable => {
                         terminator.kind = TerminatorKind::Unreachable;
@@ -309,15 +306,3 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
         }
     }
 }
-
-fn resolve_rust_intrinsic<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    func_ty: Ty<'tcx>,
-) -> Option<(Symbol, GenericArgsRef<'tcx>)> {
-    if let ty::FnDef(def_id, args) = *func_ty.kind() {
-        if tcx.is_intrinsic(def_id) {
-            return Some((tcx.item_name(def_id), args));
-        }
-    }
-    None
-}
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs
index b7cc0db9559..ac52f0ae112 100644
--- a/compiler/rustc_mir_transform/src/lower_slice_len.rs
+++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -34,67 +34,44 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     }
 }
 
-struct SliceLenPatchInformation<'tcx> {
-    add_statement: Statement<'tcx>,
-    new_terminator_kind: TerminatorKind<'tcx>,
-}
-
 fn lower_slice_len_call<'tcx>(
     tcx: TyCtxt<'tcx>,
     block: &mut BasicBlockData<'tcx>,
     local_decls: &IndexSlice<Local, LocalDecl<'tcx>>,
     slice_len_fn_item_def_id: DefId,
 ) {
-    let mut patch_found: Option<SliceLenPatchInformation<'_>> = None;
-
     let terminator = block.terminator();
-    match &terminator.kind {
-        TerminatorKind::Call {
-            func,
-            args,
-            destination,
-            target: Some(bb),
-            call_source: CallSource::Normal,
-            ..
-        } => {
-            // some heuristics for fast rejection
-            if args.len() != 1 {
-                return;
-            }
-            let Some(arg) = args[0].place() else { return };
-            let func_ty = func.ty(local_decls, tcx);
-            match func_ty.kind() {
-                ty::FnDef(fn_def_id, _) if fn_def_id == &slice_len_fn_item_def_id => {
-                    // perform modifications
-                    // from something like `_5 = core::slice::<impl [u8]>::len(move _6) -> bb1`
-                    // into:
-                    // ```
-                    // _5 = Len(*_6)
-                    // goto bb1
-                    // ```
+    if let TerminatorKind::Call {
+        func,
+        args,
+        destination,
+        target: Some(bb),
+        call_source: CallSource::Normal,
+        ..
+    } = &terminator.kind
+        // some heuristics for fast rejection
+        && let [arg] = &args[..]
+        && let Some(arg) = arg.place()
+        && let ty::FnDef(fn_def_id, _) = func.ty(local_decls, tcx).kind()
+        && *fn_def_id == slice_len_fn_item_def_id
+    {
+        // perform modifications from something like:
+        //     _5 = core::slice::<impl [u8]>::len(move _6) -> bb1
+        // into:
+        //     _5 = Len(*_6)
+        //     goto bb1
 
-                    // make new RValue for Len
-                    let deref_arg = tcx.mk_place_deref(arg);
-                    let r_value = Rvalue::Len(deref_arg);
-                    let len_statement_kind =
-                        StatementKind::Assign(Box::new((*destination, r_value)));
-                    let add_statement =
-                        Statement { kind: len_statement_kind, source_info: terminator.source_info };
+        // make new RValue for Len
+        let deref_arg = tcx.mk_place_deref(arg);
+        let r_value = Rvalue::Len(deref_arg);
+        let len_statement_kind =
+            StatementKind::Assign(Box::new((*destination, r_value)));
+        let add_statement =
+            Statement { kind: len_statement_kind, source_info: terminator.source_info };
 
-                    // modify terminator into simple Goto
-                    let new_terminator_kind = TerminatorKind::Goto { target: *bb };
-
-                    let patch = SliceLenPatchInformation { add_statement, new_terminator_kind };
-
-                    patch_found = Some(patch);
-                }
-                _ => {}
-            }
-        }
-        _ => {}
-    }
+        // modify terminator into simple Goto
+        let new_terminator_kind = TerminatorKind::Goto { target: *bb };
 
-    if let Some(SliceLenPatchInformation { add_statement, new_terminator_kind }) = patch_found {
         block.statements.push(add_statement);
         block.terminator_mut().kind = new_terminator_kind;
     }
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
index e1298b0654f..ff309bd10ec 100644
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ b/compiler/rustc_mir_transform/src/nrvo.rs
@@ -34,7 +34,7 @@ pub struct RenameReturnPlace;
 
 impl<'tcx> MirPass<'tcx> for RenameReturnPlace {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        // #111005
+        // unsound: #111005
         sess.mir_opt_level() > 0 && sess.opts.unstable_opts.unsound_mir_opts
     }
 
diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs
index fad58930e3a..8dc7b60c4e5 100644
--- a/compiler/rustc_mir_transform/src/ssa.rs
+++ b/compiler/rustc_mir_transform/src/ssa.rs
@@ -5,7 +5,6 @@
 //! As a consequence of rule 2, we consider that borrowed locals are not SSA, even if they are
 //! `Freeze`, as we do not track that the assignment dominates all uses of the borrow.
 
-use either::Either;
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_index::bit_set::BitSet;
 use rustc_index::{IndexSlice, IndexVec};
@@ -27,6 +26,12 @@ pub struct SsaLocals {
     direct_uses: IndexVec<Local, u32>,
 }
 
+pub enum AssignedValue<'a, 'tcx> {
+    Arg,
+    Rvalue(&'a mut Rvalue<'tcx>),
+    Terminator(&'a mut TerminatorKind<'tcx>),
+}
+
 impl SsaLocals {
     pub fn new<'tcx>(body: &Body<'tcx>) -> SsaLocals {
         let assignment_order = Vec::with_capacity(body.local_decls.len());
@@ -39,6 +44,7 @@ impl SsaLocals {
 
         for local in body.args_iter() {
             visitor.assignments[local] = Set1::One(DefLocation::Argument);
+            visitor.assignment_order.push(local);
         }
 
         // For SSA assignments, a RPO visit will see the assignment before it sees any use.
@@ -105,8 +111,8 @@ impl SsaLocals {
     ) -> impl Iterator<Item = (Local, &'a Rvalue<'tcx>, Location)> + 'a {
         self.assignment_order.iter().filter_map(|&local| {
             if let Set1::One(DefLocation::Body(loc)) = self.assignments[local] {
+                let stmt = body.stmt_at(loc).left()?;
                 // `loc` must point to a direct assignment to `local`.
-                let Either::Left(stmt) = body.stmt_at(loc) else { bug!() };
                 let Some((target, rvalue)) = stmt.kind.as_assign() else { bug!() };
                 assert_eq!(target.as_local(), Some(local));
                 Some((local, rvalue, loc))
@@ -118,18 +124,33 @@ impl SsaLocals {
 
     pub fn for_each_assignment_mut<'tcx>(
         &self,
-        basic_blocks: &mut BasicBlocks<'tcx>,
-        mut f: impl FnMut(Local, &mut Rvalue<'tcx>, Location),
+        basic_blocks: &mut IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
+        mut f: impl FnMut(Local, AssignedValue<'_, 'tcx>, Location),
     ) {
         for &local in &self.assignment_order {
-            if let Set1::One(DefLocation::Body(loc)) = self.assignments[local] {
-                // `loc` must point to a direct assignment to `local`.
-                let bbs = basic_blocks.as_mut_preserves_cfg();
-                let bb = &mut bbs[loc.block];
-                let stmt = &mut bb.statements[loc.statement_index];
-                let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else { bug!() };
-                assert_eq!(target.as_local(), Some(local));
-                f(local, rvalue, loc)
+            match self.assignments[local] {
+                Set1::One(DefLocation::Argument) => f(
+                    local,
+                    AssignedValue::Arg,
+                    Location { block: START_BLOCK, statement_index: 0 },
+                ),
+                Set1::One(DefLocation::Body(loc)) => {
+                    let bb = &mut basic_blocks[loc.block];
+                    let value = if loc.statement_index < bb.statements.len() {
+                        // `loc` must point to a direct assignment to `local`.
+                        let stmt = &mut bb.statements[loc.statement_index];
+                        let StatementKind::Assign(box (target, ref mut rvalue)) = stmt.kind else {
+                            bug!()
+                        };
+                        assert_eq!(target.as_local(), Some(local));
+                        AssignedValue::Rvalue(rvalue)
+                    } else {
+                        let term = bb.terminator_mut();
+                        AssignedValue::Terminator(&mut term.kind)
+                    };
+                    f(local, value, loc)
+                }
+                _ => {}
             }
         }
     }
@@ -228,8 +249,22 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> {
     }
 
     fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, loc: Location) {
-        if place.projection.first() == Some(&PlaceElem::Deref) {
-            // Do not do anything for storage statements and debuginfo.
+        let location = match ctxt {
+            PlaceContext::MutatingUse(
+                MutatingUseContext::Store | MutatingUseContext::Call | MutatingUseContext::Yield,
+            ) => Some(DefLocation::Body(loc)),
+            _ => None,
+        };
+        if let Some(location) = location
+            && let Some(local) = place.as_local()
+        {
+            self.assignments[local].insert(location);
+            if let Set1::One(_) = self.assignments[local] {
+                // Only record if SSA-like, to avoid growing the vector needlessly.
+                self.assignment_order.push(local);
+            }
+        } else if place.projection.first() == Some(&PlaceElem::Deref) {
+            // Do not do anything for debuginfo.
             if ctxt.is_use() {
                 // Only change the context if it is a real use, not a "use" in debuginfo.
                 let new_ctxt = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
@@ -237,25 +272,11 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_> {
                 self.visit_projection(place.as_ref(), new_ctxt, loc);
                 self.check_dominates(place.local, loc);
             }
-            return;
         } else {
             self.visit_projection(place.as_ref(), ctxt, loc);
             self.visit_local(place.local, ctxt, loc);
         }
     }
-
-    fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, loc: Location) {
-        if let Some(local) = place.as_local() {
-            self.assignments[local].insert(DefLocation::Body(loc));
-            if let Set1::One(_) = self.assignments[local] {
-                // Only record if SSA-like, to avoid growing the vector needlessly.
-                self.assignment_order.push(local);
-            }
-        } else {
-            self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), loc);
-        }
-        self.visit_rvalue(rvalue, loc);
-    }
 }
 
 #[instrument(level = "trace", skip(ssa, body))]
diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
index 092bcb5c979..cb028a92d49 100644
--- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
+++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
@@ -30,22 +30,17 @@ fn get_switched_on_type<'tcx>(
     let terminator = block_data.terminator();
 
     // Only bother checking blocks which terminate by switching on a local.
-    if let Some(local) = get_discriminant_local(&terminator.kind) {
-        let stmt_before_term = (!block_data.statements.is_empty())
-            .then(|| &block_data.statements[block_data.statements.len() - 1].kind);
-
-        if let Some(StatementKind::Assign(box (l, Rvalue::Discriminant(place)))) = stmt_before_term
-        {
-            if l.as_local() == Some(local) {
-                let ty = place.ty(body, tcx).ty;
-                if ty.is_enum() {
-                    return Some(ty);
-                }
-            }
-        }
+    if let Some(local) = get_discriminant_local(&terminator.kind)
+        && let [.., stmt_before_term] = &block_data.statements[..]
+        && let StatementKind::Assign(box (l, Rvalue::Discriminant(place))) = stmt_before_term.kind
+        && l.as_local() == Some(local)
+        && let ty = place.ty(body, tcx).ty
+        && ty.is_enum()
+    {
+        Some(ty)
+    } else {
+        None
     }
-
-    None
 }
 
 fn variant_discriminants<'tcx>(
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index 07910113dee..1d9dbfe4b89 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -55,16 +55,14 @@ impl<'a> TokenTreesReader<'a> {
                     let (this_spacing, next_tok) = loop {
                         let (next_tok, is_next_tok_preceded_by_whitespace) =
                             self.string_reader.next_token();
-                        if !is_next_tok_preceded_by_whitespace {
-                            if let Some(glued) = self.token.glue(&next_tok) {
-                                self.token = glued;
-                            } else {
-                                let this_spacing =
-                                    if next_tok.is_op() { Spacing::Joint } else { Spacing::Alone };
-                                break (this_spacing, next_tok);
-                            }
-                        } else {
+                        if is_next_tok_preceded_by_whitespace {
                             break (Spacing::Alone, next_tok);
+                        } else if let Some(glued) = self.token.glue(&next_tok) {
+                            self.token = glued;
+                        } else {
+                            let this_spacing =
+                                if next_tok.is_punct() { Spacing::Joint } else { Spacing::Alone };
+                            break (this_spacing, next_tok);
                         }
                     };
                     let this_tok = std::mem::replace(&mut self.token, next_tok);
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 06b1b1523ed..59498978948 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -827,6 +827,65 @@ impl<'a> Parser<'a> {
         None
     }
 
+    pub(super) fn recover_closure_body(
+        &mut self,
+        mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
+        before: token::Token,
+        prev: token::Token,
+        token: token::Token,
+        lo: Span,
+        decl_hi: Span,
+    ) -> PResult<'a, P<Expr>> {
+        err.span_label(lo.to(decl_hi), "while parsing the body of this closure");
+        match before.kind {
+            token::OpenDelim(Delimiter::Brace)
+                if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
+            {
+                // `{ || () }` should have been `|| { () }`
+                err.multipart_suggestion(
+                    "you might have meant to open the body of the closure, instead of enclosing \
+                     the closure in a block",
+                    vec![
+                        (before.span, String::new()),
+                        (prev.span.shrink_to_hi(), " {".to_string()),
+                    ],
+                    Applicability::MaybeIncorrect,
+                );
+                err.emit();
+                self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
+            }
+            token::OpenDelim(Delimiter::Parenthesis)
+                if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
+            {
+                // We are within a function call or tuple, we can emit the error
+                // and recover.
+                self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis), &token::Comma]);
+
+                err.multipart_suggestion_verbose(
+                    "you might have meant to open the body of the closure",
+                    vec![
+                        (prev.span.shrink_to_hi(), " {".to_string()),
+                        (self.token.span.shrink_to_lo(), "}".to_string()),
+                    ],
+                    Applicability::MaybeIncorrect,
+                );
+                err.emit();
+            }
+            _ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => {
+                // We don't have a heuristic to correctly identify where the block
+                // should be closed.
+                err.multipart_suggestion_verbose(
+                    "you might have meant to open the body of the closure",
+                    vec![(prev.span.shrink_to_hi(), " {".to_string())],
+                    Applicability::HasPlaceholders,
+                );
+                return Err(err);
+            }
+            _ => return Err(err),
+        }
+        Ok(self.mk_expr_err(lo.to(self.token.span)))
+    }
+
     /// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
     /// passes through any errors encountered. Used for error recovery.
     pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 91bb2d9eb66..a9f497e55e6 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1592,7 +1592,7 @@ impl<'a> Parser<'a> {
         } else if !ate_colon
             && self.may_recover()
             && (matches!(self.token.kind, token::CloseDelim(_) | token::Comma)
-                || self.token.is_op())
+                || self.token.is_punct())
         {
             let (lit, _) =
                 self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| {
@@ -2209,6 +2209,7 @@ impl<'a> Parser<'a> {
     fn parse_expr_closure(&mut self) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
 
+        let before = self.prev_token.clone();
         let binder = if self.check_keyword(kw::For) {
             let lo = self.token.span;
             let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
@@ -2239,7 +2240,12 @@ impl<'a> Parser<'a> {
             FnRetTy::Default(_) => {
                 let restrictions =
                     self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
-                self.parse_expr_res(restrictions, None)?
+                let prev = self.prev_token.clone();
+                let token = self.token.clone();
+                match self.parse_expr_res(restrictions, None) {
+                    Ok(expr) => expr,
+                    Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
+                }
             }
             _ => {
                 // If an explicit return type is given, require a block to appear (RFC 968).
@@ -2459,10 +2465,16 @@ impl<'a> Parser<'a> {
     /// Parses a `let $pat = $expr` pseudo-expression.
     fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
         let is_recovered = if !restrictions.contains(Restrictions::ALLOW_LET) {
-            Some(self.sess.emit_err(errors::ExpectedExpressionFoundLet {
+            let err = errors::ExpectedExpressionFoundLet {
                 span: self.token.span,
                 reason: ForbiddenLetReason::OtherForbidden,
-            }))
+            };
+            if self.prev_token.kind == token::BinOp(token::Or) {
+                // This was part of a closure, the that part of the parser recover.
+                return Err(err.into_diagnostic(&self.sess.span_diagnostic));
+            } else {
+                Some(self.sess.emit_err(err))
+            }
         } else {
             None
         };
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 90ac436a91f..7b6153eea09 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -9,6 +9,9 @@
     html_playground_url = "https://play.rust-lang.org/",
     test(attr(deny(warnings)))
 )]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 // We want to be able to build this crate with a stable compiler, so no
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 214c6d70960..25ef5245cf1 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -580,6 +580,8 @@ passes_outside_loop =
         *[false] {""}
     }
 
+passes_outside_loop_suggestion = consider labeling this block to be able to break within it
+
 passes_params_not_allowed =
     referencing function parameters is not allowed in naked functions
     .help = follow the calling convention in asm block to use parameters
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 52fb193f3da..fbf3dff7560 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1108,6 +1108,7 @@ impl CheckAttrVisitor<'_> {
                         | sym::html_root_url
                         | sym::html_no_source
                         | sym::test
+                        | sym::rust_logo
                             if !self.check_attr_crate_level(attr, meta, hir_id) =>
                         {
                             is_valid = false;
@@ -1166,6 +1167,18 @@ impl CheckAttrVisitor<'_> {
                         | sym::plugins
                         | sym::fake_variadic => {}
 
+                        sym::rust_logo => {
+                            if !self.tcx.features().rustdoc_internals {
+                                feature_err(
+                                    &self.tcx.sess.parse_sess,
+                                    sym::rustdoc_internals,
+                                    meta.span(),
+                                    "the `#[doc(rust_logo)]` attribute is used for Rust branding",
+                                )
+                                .emit();
+                            }
+                        }
+
                         sym::test => {
                             if !self.check_test_attr(meta, hir_id) {
                                 is_valid = false;
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index bcf5abbfe7d..4b27b6d8c36 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1099,6 +1099,16 @@ pub struct OutsideLoop<'a> {
     pub span: Span,
     pub name: &'a str,
     pub is_break: bool,
+    #[subdiagnostic]
+    pub suggestion: Option<OutsideLoopSuggestion>,
+}
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(passes_outside_loop_suggestion, applicability = "maybe-incorrect")]
+pub struct OutsideLoopSuggestion {
+    #[suggestion_part(code = "'block: ")]
+    pub block_span: Span,
+    #[suggestion_part(code = " 'block")]
+    pub break_span: Span,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 51f3c9ad76f..946a9e68da6 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -6,6 +6,9 @@
 
 #![allow(rustc::potential_query_instability)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(map_try_insert)]
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 0aaf85086e4..4590ab9e4f5 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -1,7 +1,7 @@
 use Context::*;
 
 use rustc_hir as hir;
-use rustc_hir::def_id::LocalModDefId;
+use rustc_hir::def_id::{LocalDefId, LocalModDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Destination, Movability, Node};
 use rustc_middle::hir::map::Map;
@@ -10,19 +10,21 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::hygiene::DesugaringKind;
-use rustc_span::Span;
+use rustc_span::{BytePos, Span};
 
 use crate::errors::{
     BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
-    UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
+    OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
 };
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 enum Context {
     Normal,
+    Fn,
     Loop(hir::LoopSource),
     Closure(Span),
     AsyncClosure(Span),
+    UnlabeledBlock(Span),
     LabeledBlock,
     Constant,
 }
@@ -60,6 +62,25 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
         self.with_context(Constant, |v| intravisit::walk_inline_const(v, c));
     }
 
+    fn visit_fn(
+        &mut self,
+        fk: hir::intravisit::FnKind<'hir>,
+        fd: &'hir hir::FnDecl<'hir>,
+        b: hir::BodyId,
+        _: Span,
+        id: LocalDefId,
+    ) {
+        self.with_context(Fn, |v| intravisit::walk_fn(v, fk, fd, b, id));
+    }
+
+    fn visit_trait_item(&mut self, trait_item: &'hir hir::TraitItem<'hir>) {
+        self.with_context(Fn, |v| intravisit::walk_trait_item(v, trait_item));
+    }
+
+    fn visit_impl_item(&mut self, impl_item: &'hir hir::ImplItem<'hir>) {
+        self.with_context(Fn, |v| intravisit::walk_impl_item(v, impl_item));
+    }
+
     fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
         match e.kind {
             hir::ExprKind::Loop(ref b, _, source, _) => {
@@ -83,6 +104,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
             hir::ExprKind::Block(ref b, Some(_label)) => {
                 self.with_context(LabeledBlock, |v| v.visit_block(&b));
             }
+            hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
+                self.with_context(Normal, |v| v.visit_block(&b));
+            }
+            hir::ExprKind::Block(ref b, None)
+                if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
+            {
+                self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(&b));
+            }
             hir::ExprKind::Break(break_label, ref opt_expr) => {
                 if let Some(e) = opt_expr {
                     self.visit_expr(e);
@@ -147,7 +176,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     }
                 }
 
-                self.require_break_cx("break", e.span);
+                let sp_lo = e.span.with_lo(e.span.lo() + BytePos("break".len() as u32));
+                let label_sp = match break_label.label {
+                    Some(label) => sp_lo.with_hi(label.ident.span.hi()),
+                    None => sp_lo.shrink_to_lo(),
+                };
+                self.require_break_cx("break", e.span, label_sp);
             }
             hir::ExprKind::Continue(destination) => {
                 self.require_label_in_labeled_block(e.span, &destination, "continue");
@@ -169,7 +203,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     }
                     Err(_) => {}
                 }
-                self.require_break_cx("continue", e.span)
+                self.require_break_cx("continue", e.span, e.span)
             }
             _ => intravisit::walk_expr(self, e),
         }
@@ -187,7 +221,8 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
         self.cx = old_cx;
     }
 
-    fn require_break_cx(&self, name: &str, span: Span) {
+    fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
+        let is_break = name == "break";
         match self.cx {
             LabeledBlock | Loop(_) => {}
             Closure(closure_span) => {
@@ -196,8 +231,12 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
             AsyncClosure(closure_span) => {
                 self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name });
             }
-            Normal | Constant => {
-                self.sess.emit_err(OutsideLoop { span, name, is_break: name == "break" });
+            UnlabeledBlock(block_span) if is_break && block_span.ctxt() == break_span.ctxt() => {
+                let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
+                self.sess.emit_err(OutsideLoop { span, name, is_break, suggestion });
+            }
+            Normal | Constant | Fn | UnlabeledBlock(_) => {
+                self.sess.emit_err(OutsideLoop { span, name, is_break, suggestion: None });
             }
         }
     }
diff --git a/compiler/rustc_plugin_impl/src/lib.rs b/compiler/rustc_plugin_impl/src/lib.rs
index faa7495ef9f..0e1c80a1f64 100644
--- a/compiler/rustc_plugin_impl/src/lib.rs
+++ b/compiler/rustc_plugin_impl/src/lib.rs
@@ -7,6 +7,9 @@
 //! of the Unstable Book for some examples.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![recursion_limit = "256"]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index ab85f680fcf..21d7bcbed5e 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -1,4 +1,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(associated_type_defaults)]
 #![feature(rustc_private)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index 30621a135eb..a1465dabed6 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -1,6 +1,8 @@
 //! Support for serializing the dep-graph and reloading it.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 // this shouldn't be necessary, but the check for `&mut _` is too naive and denies returning a function pointer that takes a mut ref
 #![feature(const_mut_refs)]
 #![feature(const_refs_to_cell)]
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 5f012ec29fe..0bc45967e3d 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -7,6 +7,8 @@
 //! Type-relative name resolution (methods, fields, associated items) happens in `rustc_hir_analysis`.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
 #![feature(extract_if)]
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 5360aa9ea6a..cfa54072eb9 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -5,6 +5,9 @@
     html_playground_url = "https://play.rust-lang.org/",
     test(attr(allow(unused_variables), deny(warnings)))
 )]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(allocator_api)]
 #![feature(associated_type_bounds)]
 #![feature(const_option)]
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index e26d25d9a41..3af83aaaaa8 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-bitflags = "1.2.1"
 getopts = "0.2"
 rustc_macros = { path = "../rustc_macros" }
 tracing = "0.1"
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index b356b503aa5..fa1b6f9f13d 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -5,9 +5,6 @@ session_cannot_enable_crt_static_linux = sanitizer is incompatible with statical
 
 session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}`
 
-session_cgu_not_recorded =
-    CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded
-
 session_cli_feature_diagnostic_help =
     add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable
 
@@ -34,12 +31,6 @@ session_hexadecimal_float_literal_not_supported = hexadecimal float literal is n
 session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target
     .note = compatible flavors are: {$compatible_list}
 
-session_incorrect_cgu_reuse_type =
-    CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
-    [one] {"at least "}
-    *[other] {""}
-    }`{$expected_reuse}`
-
 session_instrumentation_not_supported = {$us} instrumentation is not supported for this target
 
 session_int_literal_too_large = integer literal is too large
diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs
deleted file mode 100644
index 8703e575465..00000000000
--- a/compiler/rustc_session/src/cgu_reuse_tracker.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-//! Some facilities for tracking how codegen-units are reused during incremental
-//! compilation. This is used for incremental compilation tests and debug
-//! output.
-
-use crate::errors::{CguNotRecorded, IncorrectCguReuseType};
-use crate::Session;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
-use rustc_span::{Span, Symbol};
-use std::borrow::Cow;
-use std::fmt::{self};
-use std::sync::{Arc, Mutex};
-
-#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
-pub enum CguReuse {
-    No,
-    PreLto,
-    PostLto,
-}
-
-impl fmt::Display for CguReuse {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            CguReuse::No => write!(f, "No"),
-            CguReuse::PreLto => write!(f, "PreLto "),
-            CguReuse::PostLto => write!(f, "PostLto "),
-        }
-    }
-}
-
-impl IntoDiagnosticArg for CguReuse {
-    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
-        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum ComparisonKind {
-    Exact,
-    AtLeast,
-}
-
-struct TrackerData {
-    actual_reuse: FxHashMap<String, CguReuse>,
-    expected_reuse: FxHashMap<String, (String, SendSpan, CguReuse, ComparisonKind)>,
-}
-
-// Span does not implement `Send`, so we can't just store it in the shared
-// `TrackerData` object. Instead of splitting up `TrackerData` into shared and
-// non-shared parts (which would be complicated), we just mark the `Span` here
-// explicitly as `Send`. That's safe because the span data here is only ever
-// accessed from the main thread.
-struct SendSpan(Span);
-unsafe impl Send for SendSpan {}
-
-#[derive(Clone)]
-pub struct CguReuseTracker {
-    data: Option<Arc<Mutex<TrackerData>>>,
-}
-
-impl CguReuseTracker {
-    pub fn new() -> CguReuseTracker {
-        let data =
-            TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() };
-
-        CguReuseTracker { data: Some(Arc::new(Mutex::new(data))) }
-    }
-
-    pub fn new_disabled() -> CguReuseTracker {
-        CguReuseTracker { data: None }
-    }
-
-    pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) {
-        if let Some(ref data) = self.data {
-            debug!("set_actual_reuse({cgu_name:?}, {kind:?})");
-
-            let prev_reuse = data.lock().unwrap().actual_reuse.insert(cgu_name.to_string(), kind);
-
-            if let Some(prev_reuse) = prev_reuse {
-                // The only time it is legal to overwrite reuse state is when
-                // we discover during ThinLTO that we can actually reuse the
-                // post-LTO version of a CGU.
-                assert_eq!(prev_reuse, CguReuse::PreLto);
-            }
-        }
-    }
-
-    pub fn set_expectation(
-        &self,
-        cgu_name: Symbol,
-        cgu_user_name: &str,
-        error_span: Span,
-        expected_reuse: CguReuse,
-        comparison_kind: ComparisonKind,
-    ) {
-        if let Some(ref data) = self.data {
-            debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})");
-            let mut data = data.lock().unwrap();
-
-            data.expected_reuse.insert(
-                cgu_name.to_string(),
-                (cgu_user_name.to_string(), SendSpan(error_span), expected_reuse, comparison_kind),
-            );
-        }
-    }
-
-    pub fn check_expected_reuse(&self, sess: &Session) {
-        if let Some(ref data) = self.data {
-            let data = data.lock().unwrap();
-
-            for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in
-                &data.expected_reuse
-            {
-                if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) {
-                    let (error, at_least) = match comparison_kind {
-                        ComparisonKind::Exact => (expected_reuse != actual_reuse, false),
-                        ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true),
-                    };
-
-                    if error {
-                        let at_least = if at_least { 1 } else { 0 };
-                        IncorrectCguReuseType {
-                            span: error_span.0,
-                            cgu_user_name,
-                            actual_reuse,
-                            expected_reuse,
-                            at_least,
-                        };
-                    }
-                } else {
-                    sess.emit_fatal(CguNotRecorded { cgu_user_name, cgu_name });
-                }
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 3970b751af7..2a6f5994c49 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -12,6 +12,7 @@ use crate::{EarlyErrorHandler, Session};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
 use rustc_target::abi::Align;
+use rustc_target::spec::LinkSelfContainedComponents;
 use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, SplitDebuginfo};
 use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS};
 
@@ -232,63 +233,35 @@ pub struct LinkSelfContained {
     /// Used for compatibility with the existing opt-in and target inference.
     pub explicitly_set: Option<bool>,
 
-    /// The components that are enabled.
-    components: LinkSelfContainedComponents,
-}
-
-bitflags::bitflags! {
-    #[derive(Default)]
-    /// The `-C link-self-contained` components that can individually be enabled or disabled.
-    pub struct LinkSelfContainedComponents: u8 {
-        /// CRT objects (e.g. on `windows-gnu`, `musl`, `wasi` targets)
-        const CRT_OBJECTS = 1 << 0;
-        /// libc static library (e.g. on `musl`, `wasi` targets)
-        const LIBC        = 1 << 1;
-        /// libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets)
-        const UNWIND      = 1 << 2;
-        /// Linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for `rust-lld`)
-        const LINKER      = 1 << 3;
-        /// Sanitizer runtime libraries
-        const SANITIZERS  = 1 << 4;
-        /// Other MinGW libs and Windows import libs
-        const MINGW       = 1 << 5;
-    }
-}
-
-impl FromStr for LinkSelfContainedComponents {
-    type Err = ();
+    /// The components that are enabled on the CLI, using the `+component` syntax or one of the
+    /// `true` shorcuts.
+    enabled_components: LinkSelfContainedComponents,
 
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        Ok(match s {
-            "crto" => LinkSelfContainedComponents::CRT_OBJECTS,
-            "libc" => LinkSelfContainedComponents::LIBC,
-            "unwind" => LinkSelfContainedComponents::UNWIND,
-            "linker" => LinkSelfContainedComponents::LINKER,
-            "sanitizers" => LinkSelfContainedComponents::SANITIZERS,
-            "mingw" => LinkSelfContainedComponents::MINGW,
-            _ => return Err(()),
-        })
-    }
+    /// The components that are disabled on the CLI, using the `-component` syntax or one of the
+    /// `false` shortcuts.
+    disabled_components: LinkSelfContainedComponents,
 }
 
 impl LinkSelfContained {
     /// Incorporates an enabled or disabled component as specified on the CLI, if possible.
     /// For example: `+linker`, and `-crto`.
-    pub(crate) fn handle_cli_component(&mut self, component: &str) -> Result<(), ()> {
+    pub(crate) fn handle_cli_component(&mut self, component: &str) -> Option<()> {
         // Note that for example `-Cself-contained=y -Cself-contained=-linker` is not an explicit
         // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been
         // set in bulk with its historical values, then manually setting a component clears that
         // `explicitly_set` state.
         if let Some(component_to_enable) = component.strip_prefix('+') {
             self.explicitly_set = None;
-            self.components.insert(component_to_enable.parse()?);
-            Ok(())
+            self.enabled_components
+                .insert(LinkSelfContainedComponents::from_str(component_to_enable)?);
+            Some(())
         } else if let Some(component_to_disable) = component.strip_prefix('-') {
             self.explicitly_set = None;
-            self.components.remove(component_to_disable.parse()?);
-            Ok(())
+            self.disabled_components
+                .insert(LinkSelfContainedComponents::from_str(component_to_disable)?);
+            Some(())
         } else {
-            Err(())
+            None
         }
     }
 
@@ -296,11 +269,14 @@ impl LinkSelfContained {
     /// purposes.
     pub(crate) fn set_all_explicitly(&mut self, enabled: bool) {
         self.explicitly_set = Some(enabled);
-        self.components = if enabled {
-            LinkSelfContainedComponents::all()
+
+        if enabled {
+            self.enabled_components = LinkSelfContainedComponents::all();
+            self.disabled_components = LinkSelfContainedComponents::empty();
         } else {
-            LinkSelfContainedComponents::empty()
-        };
+            self.enabled_components = LinkSelfContainedComponents::empty();
+            self.disabled_components = LinkSelfContainedComponents::all();
+        }
     }
 
     /// Helper creating a fully enabled `LinkSelfContained` instance. Used in tests.
@@ -314,13 +290,32 @@ impl LinkSelfContained {
     /// components was set individually. This would also require the `-Zunstable-options` flag, to
     /// be allowed.
     fn are_unstable_variants_set(&self) -> bool {
-        let any_component_set = !self.components.is_empty();
+        let any_component_set =
+            !self.enabled_components.is_empty() || !self.disabled_components.is_empty();
         self.explicitly_set.is_none() && any_component_set
     }
 
-    /// Returns whether the self-contained linker component is enabled.
-    pub fn linker(&self) -> bool {
-        self.components.contains(LinkSelfContainedComponents::LINKER)
+    /// Returns whether the self-contained linker component was enabled on the CLI, using the
+    /// `-C link-self-contained=+linker` syntax, or one of the `true` shorcuts.
+    pub fn is_linker_enabled(&self) -> bool {
+        self.enabled_components.contains(LinkSelfContainedComponents::LINKER)
+    }
+
+    /// Returns whether the self-contained linker component was disabled on the CLI, using the
+    /// `-C link-self-contained=-linker` syntax, or one of the `false` shorcuts.
+    pub fn is_linker_disabled(&self) -> bool {
+        self.disabled_components.contains(LinkSelfContainedComponents::LINKER)
+    }
+
+    /// Returns CLI inconsistencies to emit errors: individual components were both enabled and
+    /// disabled.
+    fn check_consistency(&self) -> Option<LinkSelfContainedComponents> {
+        if self.explicitly_set.is_some() {
+            None
+        } else {
+            let common = self.enabled_components.intersection(self.disabled_components);
+            if common.is_empty() { None } else { Some(common) }
+        }
     }
 }
 
@@ -2758,9 +2753,8 @@ pub fn build_session_options(
     }
 
     // For testing purposes, until we have more feedback about these options: ensure `-Z
-    // unstable-options` is required when using the unstable `-C link-self-contained` options, like
-    // `-C link-self-contained=+linker`, and when using the unstable `-C linker-flavor` options, like
-    // `-C linker-flavor=gnu-lld-cc`.
+    // unstable-options` is required when using the unstable `-C link-self-contained` and `-C
+    // linker-flavor` options.
     if !nightly_options::is_unstable_enabled(matches) {
         let uses_unstable_self_contained_option =
             cg.link_self_contained.are_unstable_variants_set();
@@ -2782,6 +2776,19 @@ pub fn build_session_options(
         }
     }
 
+    // Check `-C link-self-contained` for consistency: individual components cannot be both enabled
+    // and disabled at the same time.
+    if let Some(erroneous_components) = cg.link_self_contained.check_consistency() {
+        let names: String = erroneous_components
+            .into_iter()
+            .map(|c| c.as_str().unwrap())
+            .intersperse(", ")
+            .collect();
+        handler.early_error(format!(
+            "some `-C link-self-contained` components were both enabled and disabled: {names}"
+        ));
+    }
+
     let prints = collect_print_requests(handler, &mut cg, &mut unstable_opts, matches);
 
     let cg = cg;
@@ -2918,8 +2925,8 @@ fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) ->
         "expanded" => Source(PpSourceMode::Expanded),
         "expanded,identified" => Source(PpSourceMode::ExpandedIdentified),
         "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene),
-        "ast-tree" => AstTree(PpAstTreeMode::Normal),
-        "ast-tree,expanded" => AstTree(PpAstTreeMode::Expanded),
+        "ast-tree" => AstTree,
+        "ast-tree,expanded" => AstTreeExpanded,
         "hir" => Hir(PpHirMode::Normal),
         "hir,identified" => Hir(PpHirMode::Identified),
         "hir,typed" => Hir(PpHirMode::Typed),
@@ -3077,14 +3084,6 @@ pub enum PpSourceMode {
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
-pub enum PpAstTreeMode {
-    /// `-Zunpretty=ast`
-    Normal,
-    /// `-Zunpretty=ast,expanded`
-    Expanded,
-}
-
-#[derive(Copy, Clone, PartialEq, Debug)]
 pub enum PpHirMode {
     /// `-Zunpretty=hir`
     Normal,
@@ -3099,7 +3098,10 @@ pub enum PpMode {
     /// Options that print the source code, i.e.
     /// `-Zunpretty=normal` and `-Zunpretty=expanded`
     Source(PpSourceMode),
-    AstTree(PpAstTreeMode),
+    /// `-Zunpretty=ast-tree`
+    AstTree,
+    /// `-Zunpretty=ast-tree,expanded`
+    AstTreeExpanded,
     /// Options that print the HIR, i.e. `-Zunpretty=hir`
     Hir(PpHirMode),
     /// `-Zunpretty=hir-tree`
@@ -3119,10 +3121,10 @@ impl PpMode {
         use PpMode::*;
         use PpSourceMode::*;
         match *self {
-            Source(Normal | Identified) | AstTree(PpAstTreeMode::Normal) => false,
+            Source(Normal | Identified) | AstTree => false,
 
             Source(Expanded | ExpandedIdentified | ExpandedHygiene)
-            | AstTree(PpAstTreeMode::Expanded)
+            | AstTreeExpanded
             | Hir(_)
             | HirTree
             | ThirTree
@@ -3134,7 +3136,7 @@ impl PpMode {
     pub fn needs_hir(&self) -> bool {
         use PpMode::*;
         match *self {
-            Source(_) | AstTree(_) => false,
+            Source(_) | AstTree | AstTreeExpanded => false,
 
             Hir(_) | HirTree | ThirTree | ThirFlat | Mir | MirCFG => true,
         }
@@ -3142,7 +3144,7 @@ impl PpMode {
 
     pub fn needs_analysis(&self) -> bool {
         use PpMode::*;
-        matches!(*self, Mir | MirCFG | ThirTree | ThirFlat)
+        matches!(*self, Hir(PpHirMode::Typed) | Mir | MirCFG | ThirTree | ThirFlat)
     }
 }
 
@@ -3165,6 +3167,7 @@ impl PpMode {
 /// we have an opt-in scheme here, so one is hopefully forced to think about
 /// how the hash should be calculated when adding a new command-line argument.
 pub(crate) mod dep_tracking {
+    use super::Polonius;
     use super::{
         BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression,
         ErrorOutputType, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
@@ -3274,6 +3277,7 @@ pub(crate) mod dep_tracking {
         OomStrategy,
         LanguageIdentifier,
         TraitSolver,
+        Polonius,
     );
 
     impl<T1, T2> DepTrackingHash for (T1, T2)
@@ -3412,3 +3416,35 @@ impl DumpMonoStatsFormat {
         }
     }
 }
+
+/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
+/// or future prototype.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum Polonius {
+    /// The default value: disabled.
+    Off,
+
+    /// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`.
+    Legacy,
+
+    /// In-tree prototype, extending the NLL infrastructure.
+    Next,
+}
+
+impl Default for Polonius {
+    fn default() -> Self {
+        Polonius::Off
+    }
+}
+
+impl Polonius {
+    /// Returns whether the legacy version of polonius is enabled
+    pub fn is_legacy_enabled(&self) -> bool {
+        matches!(self, Polonius::Legacy)
+    }
+
+    /// Returns whether the "next" version of polonius is enabled
+    pub fn is_next_enabled(&self) -> bool {
+        matches!(self, Polonius::Next)
+    }
+}
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 5f8bbfca890..31094e0d266 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -1,6 +1,5 @@
 use std::num::NonZeroU32;
 
-use crate::cgu_reuse_tracker::CguReuse;
 use crate::parse::ParseSess;
 use rustc_ast::token;
 use rustc_ast::util::literal::LitError;
@@ -9,24 +8,6 @@ use rustc_macros::Diagnostic;
 use rustc_span::{BytePos, Span, Symbol};
 use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
 
-#[derive(Diagnostic)]
-#[diag(session_incorrect_cgu_reuse_type)]
-pub struct IncorrectCguReuseType<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub cgu_user_name: &'a str,
-    pub actual_reuse: CguReuse,
-    pub expected_reuse: CguReuse,
-    pub at_least: u8,
-}
-
-#[derive(Diagnostic)]
-#[diag(session_cgu_not_recorded)]
-pub struct CguNotRecorded<'a> {
-    pub cgu_user_name: &'a str,
-    pub cgu_name: &'a str,
-}
-
 pub struct FeatureGateError {
     pub span: MultiSpan,
     pub explain: DiagnosticMessage,
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index d6c746a7bd8..7da0bcf01bf 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(option_get_or_insert_default)]
 #![feature(rustc_attrs)]
 #![feature(map_many_mut)]
+#![feature(iter_intersperse)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 #![deny(rustc::untranslatable_diagnostic)]
@@ -22,7 +23,6 @@ extern crate tracing;
 use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_fluent_macro::fluent_messages;
 
-pub mod cgu_reuse_tracker;
 pub mod utils;
 pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass};
 pub use rustc_lint_defs as lint;
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index f7c000c8bd6..eb1aa6d6c88 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -414,6 +414,7 @@ mod desc {
         "one of supported split dwarf modes (`split` or `single`)";
     pub const parse_link_self_contained: &str = "one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) \
         components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw`";
+    pub const parse_polonius: &str = "either no value or `legacy` (the default), or `next`";
     pub const parse_stack_protector: &str =
         "one of (`none` (default), `basic`, `strong`, or `all`)";
     pub const parse_branch_protection: &str =
@@ -471,6 +472,21 @@ mod parse {
         }
     }
 
+    /// Parses whether polonius is enabled, and if so, which version.
+    pub(crate) fn parse_polonius(slot: &mut Polonius, v: Option<&str>) -> bool {
+        match v {
+            Some("legacy") | None => {
+                *slot = Polonius::Legacy;
+                true
+            }
+            Some("next") => {
+                *slot = Polonius::Next;
+                true
+            }
+            _ => false,
+        }
+    }
+
     /// Use this for any string option that has a static default.
     pub(crate) fn parse_string(slot: &mut String, v: Option<&str>) -> bool {
         match v {
@@ -1165,7 +1181,7 @@ mod parse {
 
         // 2. Parse a list of enabled and disabled components.
         for comp in s.split(',') {
-            if slot.handle_cli_component(comp).is_err() {
+            if slot.handle_cli_component(comp).is_none() {
                 return false;
             }
         }
@@ -1599,9 +1615,10 @@ options! {
         "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
         (default: no)"),
     mir_enable_passes: Vec<(String, bool)> = (Vec::new(), parse_list_with_polarity, [TRACKED],
-        "use like `-Zmir-enable-passes=+DestinationPropagation,-InstSimplify`. Forces the specified passes to be \
-        enabled, overriding all other checks. Passes that are not specified are enabled or \
-        disabled by other flags as usual."),
+        "use like `-Zmir-enable-passes=+DestinationPropagation,-InstSimplify`. Forces the \
+        specified passes to be enabled, overriding all other checks. In particular, this will \
+        enable unsound (known-buggy and hence usually disabled) passes without further warning! \
+        Passes that are not specified are enabled or disabled by other flags as usual."),
     mir_include_spans: bool = (false, parse_bool, [UNTRACKED],
         "use line numbers relative to the function in mir pretty printing"),
     mir_keep_place_mention: bool = (false, parse_bool, [TRACKED],
@@ -1658,7 +1675,7 @@ options! {
         "whether to use the PLT when calling into shared libraries;
         only has effect for PIC code on systems with ELF binaries
         (default: PLT is disabled if full relro is enabled on x86_64)"),
-    polonius: bool = (false, parse_bool, [TRACKED],
+    polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED],
         "enable polonius-based borrow-checker (default: no)"),
     polymorphize: bool = (false, parse_bool, [TRACKED],
           "perform polymorphization analysis"),
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index b484978eed2..5cac11cc8f7 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1,4 +1,3 @@
-use crate::cgu_reuse_tracker::CguReuseTracker;
 use crate::code_stats::CodeStats;
 pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
 use crate::config::{
@@ -153,9 +152,6 @@ pub struct Session {
     pub io: CompilerIO,
 
     incr_comp_session: OneThread<RefCell<IncrCompSession>>,
-    /// Used for incremental compilation tests. Will only be populated if
-    /// `-Zquery-dep-graph` is specified.
-    pub cgu_reuse_tracker: CguReuseTracker,
 
     /// Used by `-Z self-profile`.
     pub prof: SelfProfilerRef,
@@ -1431,12 +1427,6 @@ pub fn build_session(
     });
     let print_fuel = AtomicU64::new(0);
 
-    let cgu_reuse_tracker = if sopts.unstable_opts.query_dep_graph {
-        CguReuseTracker::new()
-    } else {
-        CguReuseTracker::new_disabled()
-    };
-
     let prof = SelfProfilerRef::new(
         self_profiler,
         sopts.unstable_opts.time_passes.then(|| sopts.unstable_opts.time_passes_format),
@@ -1461,7 +1451,6 @@ pub fn build_session(
         sysroot,
         io,
         incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
-        cgu_reuse_tracker,
         prof,
         perf_stats: PerfStats {
             symbol_hash_time: Lock::new(Duration::from_secs(0)),
diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index 3e0d6baab6a..2b77044d6bf 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
+rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_driver = { path = "../rustc_driver" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_interface = { path = "../rustc_interface" }
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index d10f46fad9e..c24b9efe865 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -10,6 +10,9 @@
     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(attr(allow(unused_variables), deny(warnings)))
 )]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 
 pub mod rustc_internal;
 
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 36eb2247253..e3c84f06543 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -3,24 +3,27 @@
 //! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs
 //! until stable MIR is complete.
 
-use std::ops::{ControlFlow, Index};
-
 use crate::rustc_internal;
 use crate::rustc_smir::Tables;
+use rustc_data_structures::fx;
 use rustc_driver::{Callbacks, Compilation, RunCompiler};
 use rustc_interface::{interface, Queries};
 use rustc_middle::mir::interpret::AllocId;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{CrateNum, DefId};
 use rustc_span::Span;
+use stable_mir::ty::IndexedVal;
 use stable_mir::CompilerError;
+use std::fmt::Debug;
+use std::hash::Hash;
+use std::ops::{ControlFlow, Index};
 
 impl<'tcx> Index<stable_mir::DefId> for Tables<'tcx> {
     type Output = DefId;
 
     #[inline(always)]
     fn index(&self, index: stable_mir::DefId) -> &Self::Output {
-        &self.def_ids[index.0]
+        &self.def_ids[index]
     }
 }
 
@@ -29,7 +32,7 @@ impl<'tcx> Index<stable_mir::ty::Span> for Tables<'tcx> {
 
     #[inline(always)]
     fn index(&self, index: stable_mir::ty::Span) -> &Self::Output {
-        &self.spans[index.0]
+        &self.spans[index]
     }
 }
 
@@ -95,36 +98,15 @@ impl<'tcx> Tables<'tcx> {
     }
 
     fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
-        // FIXME: this becomes inefficient when we have too many ids
-        for (i, &d) in self.def_ids.iter().enumerate() {
-            if d == did {
-                return stable_mir::DefId(i);
-            }
-        }
-        let id = self.def_ids.len();
-        self.def_ids.push(did);
-        stable_mir::DefId(id)
+        self.def_ids.create_or_fetch(did)
     }
 
     fn create_alloc_id(&mut self, aid: AllocId) -> stable_mir::AllocId {
-        // FIXME: this becomes inefficient when we have too many ids
-        if let Some(i) = self.alloc_ids.iter().position(|a| *a == aid) {
-            return stable_mir::AllocId(i);
-        };
-        let id = self.def_ids.len();
-        self.alloc_ids.push(aid);
-        stable_mir::AllocId(id)
+        self.alloc_ids.create_or_fetch(aid)
     }
 
     pub(crate) fn create_span(&mut self, span: Span) -> stable_mir::ty::Span {
-        for (i, &sp) in self.spans.iter().enumerate() {
-            if sp == span {
-                return stable_mir::ty::Span(i);
-            }
-        }
-        let id = self.spans.len();
-        self.spans.push(span);
-        stable_mir::ty::Span(id)
+        self.spans.create_or_fetch(span)
     }
 }
 
@@ -134,7 +116,13 @@ pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
 
 pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) {
     stable_mir::run(
-        Tables { tcx, def_ids: vec![], alloc_ids: vec![], spans: vec![], types: vec![] },
+        Tables {
+            tcx,
+            def_ids: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
+            alloc_ids: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
+            spans: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
+            types: vec![],
+        },
         f,
     );
 }
@@ -197,3 +185,29 @@ where
         })
     }
 }
+
+/// Simmilar to rustc's `FxIndexMap`, `IndexMap` with extra
+/// safety features added.
+pub struct IndexMap<K, V> {
+    index_map: fx::FxIndexMap<K, V>,
+}
+
+impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> IndexMap<K, V> {
+    pub fn create_or_fetch(&mut self, key: K) -> V {
+        let len = self.index_map.len();
+        let v = self.index_map.entry(key).or_insert(V::to_val(len));
+        *v
+    }
+}
+
+impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> Index<V>
+    for IndexMap<K, V>
+{
+    type Output = K;
+
+    fn index(&self, index: V) -> &Self::Output {
+        let (k, v) = self.index_map.get_index(index.to_index()).unwrap();
+        assert_eq!(*v, index, "Provided value doesn't match with indexed value");
+        k
+    }
+}
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 7d1122c2fd2..f26d18ad38f 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -7,6 +7,7 @@
 //!
 //! For now, we are developing everything inside `rustc`, thus, we keep this module private.
 
+use crate::rustc_internal::IndexMap;
 use crate::rustc_smir::hir::def::DefKind;
 use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyBoundRegion, Region};
 use rustc_hir as hir;
@@ -16,8 +17,10 @@ use rustc_middle::ty::{self, Ty, TyCtxt, Variance};
 use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc_target::abi::FieldIdx;
 use stable_mir::mir::{CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx};
-use stable_mir::ty::{FloatTy, GenericParamDef, IntTy, Movability, RigidTy, Span, TyKind, UintTy};
-use stable_mir::{self, opaque, Context};
+use stable_mir::ty::{
+    FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, Span, TyKind, UintTy,
+};
+use stable_mir::{self, opaque, Context, Filename};
 use tracing::debug;
 
 mod alloc;
@@ -31,21 +34,45 @@ impl<'tcx> Context for Tables<'tcx> {
         self.tcx.crates(()).iter().map(|crate_num| smir_crate(self.tcx, *crate_num)).collect()
     }
 
-    fn find_crate(&self, name: &str) -> Option<stable_mir::Crate> {
-        [LOCAL_CRATE].iter().chain(self.tcx.crates(()).iter()).find_map(|crate_num| {
-            let crate_name = self.tcx.crate_name(*crate_num).to_string();
-            (name == crate_name).then(|| smir_crate(self.tcx, *crate_num))
-        })
+    fn find_crates(&self, name: &str) -> Vec<stable_mir::Crate> {
+        let crates: Vec<stable_mir::Crate> = [LOCAL_CRATE]
+            .iter()
+            .chain(self.tcx.crates(()).iter())
+            .map(|crate_num| {
+                let crate_name = self.tcx.crate_name(*crate_num).to_string();
+                (name == crate_name).then(|| smir_crate(self.tcx, *crate_num))
+            })
+            .into_iter()
+            .filter_map(|c| c)
+            .collect();
+        crates
     }
 
     fn name_of_def_id(&self, def_id: stable_mir::DefId) -> String {
         self.tcx.def_path_str(self[def_id])
     }
 
-    fn print_span(&self, span: stable_mir::ty::Span) -> String {
+    fn span_to_string(&self, span: stable_mir::ty::Span) -> String {
         self.tcx.sess.source_map().span_to_diagnostic_string(self[span])
     }
 
+    fn get_filename(&self, span: &Span) -> Filename {
+        opaque(
+            &self
+                .tcx
+                .sess
+                .source_map()
+                .span_to_filename(self[*span])
+                .display(rustc_span::FileNameDisplayPreference::Local)
+                .to_string(),
+        )
+    }
+
+    fn get_lines(&self, span: &Span) -> LineInfo {
+        let lines = &self.tcx.sess.source_map().span_to_location_info(self[*span]);
+        LineInfo { start_line: lines.1, start_col: lines.2, end_line: lines.3, end_col: lines.4 }
+    }
+
     fn def_kind(&mut self, def_id: stable_mir::DefId) -> stable_mir::DefKind {
         self.tcx.def_kind(self[def_id]).stable(self)
     }
@@ -194,9 +221,9 @@ impl<S, R: PartialEq> PartialEq<R> for MaybeStable<S, R> {
 
 pub struct Tables<'tcx> {
     pub tcx: TyCtxt<'tcx>,
-    pub def_ids: Vec<DefId>,
-    pub alloc_ids: Vec<AllocId>,
-    pub spans: Vec<rustc_span::Span>,
+    pub def_ids: IndexMap<DefId, stable_mir::DefId>,
+    pub alloc_ids: IndexMap<AllocId, stable_mir::AllocId>,
+    pub spans: IndexMap<rustc_span::Span, Span>,
     pub types: Vec<MaybeStable<stable_mir::ty::TyKind, Ty<'tcx>>>,
 }
 
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 6fd61e45fcc..e62efab5793 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -14,6 +14,8 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(array_windows)]
 #![feature(if_let_guard)]
 #![feature(negative_impls)]
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index a62fa246ac4..ea261923c65 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1327,6 +1327,7 @@ symbols! {
         rust_cold_cc,
         rust_eh_catch_typeinfo,
         rust_eh_personality,
+        rust_logo,
         rustc,
         rustc_abi,
         rustc_allocator,
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index 535a3ea2d7e..6ade2d777c7 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -88,6 +88,9 @@
 //! DefPaths which are much more robust in the face of changes to the code base.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(never_type)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index e838e11131f..9c5ce889418 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -8,6 +8,8 @@
 //! LLVM.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
 #![feature(assert_matches)]
 #![feature(associated_type_bounds)]
 #![feature(exhaustive_patterns)]
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index d4774386e2e..16f70cf43b3 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -164,11 +164,11 @@ pub enum LinkerFlavor {
 
 /// Linker flavors available externally through command line (`-Clinker-flavor`)
 /// or json target specifications.
-/// FIXME: This set has accumulated historically, bring it more in line with the internal
-/// linker flavors (`LinkerFlavor`).
+/// This set has accumulated historically, and contains both (stable and unstable) legacy values, as
+/// well as modern ones matching the internal linker flavors (`LinkerFlavor`).
 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
 pub enum LinkerFlavorCli {
-    // New (unstable) flavors, with direct counterparts in `LinkerFlavor`.
+    // Modern (unstable) flavors, with direct counterparts in `LinkerFlavor`.
     Gnu(Cc, Lld),
     Darwin(Cc, Lld),
     WasmLld(Cc),
@@ -179,7 +179,7 @@ pub enum LinkerFlavorCli {
     Bpf,
     Ptx,
 
-    // Below: the legacy stable values.
+    // Legacy stable values
     Gcc,
     Ld,
     Lld(LldFlavor),
@@ -504,7 +504,7 @@ linker_flavor_cli_impls! {
     (LinkerFlavorCli::Bpf) "bpf"
     (LinkerFlavorCli::Ptx) "ptx"
 
-    // Below: legacy stable values
+    // Legacy stable flavors
     (LinkerFlavorCli::Gcc) "gcc"
     (LinkerFlavorCli::Ld) "ld"
     (LinkerFlavorCli::Lld(LldFlavor::Ld)) "ld.lld"
@@ -520,6 +520,80 @@ impl ToJson for LinkerFlavorCli {
     }
 }
 
+bitflags::bitflags! {
+    #[derive(Default)]
+    /// The `-C link-self-contained` components that can individually be enabled or disabled.
+    pub struct LinkSelfContainedComponents: u8 {
+        /// CRT objects (e.g. on `windows-gnu`, `musl`, `wasi` targets)
+        const CRT_OBJECTS = 1 << 0;
+        /// libc static library (e.g. on `musl`, `wasi` targets)
+        const LIBC        = 1 << 1;
+        /// libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets)
+        const UNWIND      = 1 << 2;
+        /// Linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for `rust-lld`)
+        const LINKER      = 1 << 3;
+        /// Sanitizer runtime libraries
+        const SANITIZERS  = 1 << 4;
+        /// Other MinGW libs and Windows import libs
+        const MINGW       = 1 << 5;
+    }
+}
+
+impl LinkSelfContainedComponents {
+    /// Parses a single `-Clink-self-contained` well-known component, not a set of flags.
+    pub fn from_str(s: &str) -> Option<LinkSelfContainedComponents> {
+        Some(match s {
+            "crto" => LinkSelfContainedComponents::CRT_OBJECTS,
+            "libc" => LinkSelfContainedComponents::LIBC,
+            "unwind" => LinkSelfContainedComponents::UNWIND,
+            "linker" => LinkSelfContainedComponents::LINKER,
+            "sanitizers" => LinkSelfContainedComponents::SANITIZERS,
+            "mingw" => LinkSelfContainedComponents::MINGW,
+            _ => return None,
+        })
+    }
+
+    /// Return the component's name.
+    ///
+    /// Returns `None` if the bitflags aren't a singular component (but a mix of multiple flags).
+    pub fn as_str(self) -> Option<&'static str> {
+        Some(match self {
+            LinkSelfContainedComponents::CRT_OBJECTS => "crto",
+            LinkSelfContainedComponents::LIBC => "libc",
+            LinkSelfContainedComponents::UNWIND => "unwind",
+            LinkSelfContainedComponents::LINKER => "linker",
+            LinkSelfContainedComponents::SANITIZERS => "sanitizers",
+            LinkSelfContainedComponents::MINGW => "mingw",
+            _ => return None,
+        })
+    }
+
+    /// Returns an array of all the components.
+    fn all_components() -> [LinkSelfContainedComponents; 6] {
+        [
+            LinkSelfContainedComponents::CRT_OBJECTS,
+            LinkSelfContainedComponents::LIBC,
+            LinkSelfContainedComponents::UNWIND,
+            LinkSelfContainedComponents::LINKER,
+            LinkSelfContainedComponents::SANITIZERS,
+            LinkSelfContainedComponents::MINGW,
+        ]
+    }
+}
+
+impl IntoIterator for LinkSelfContainedComponents {
+    type Item = LinkSelfContainedComponents;
+    type IntoIter = std::vec::IntoIter<LinkSelfContainedComponents>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        LinkSelfContainedComponents::all_components()
+            .into_iter()
+            .filter(|&s| self.contains(s))
+            .collect::<Vec<_>>()
+            .into_iter()
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
 pub enum PanicStrategy {
     Unwind,
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 56d37d58de7..5ba29f87855 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -11,6 +11,9 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(associated_type_bounds)]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index 066129d8e47..066129d8e47 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 77a3b5e1284..d45fe102805 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -32,14 +32,11 @@ mod assembly;
 mod canonicalize;
 mod eval_ctxt;
 mod fulfill;
-mod inherent_projection;
 pub mod inspect;
 mod normalize;
-mod opaques;
 mod project_goals;
 mod search_graph;
 mod trait_goals;
-mod weak_types;
 
 pub use eval_ctxt::{
     EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
diff --git a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs b/compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs
index 28fe59b7f6a..28fe59b7f6a 100644
--- a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/inherent_projection.rs
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs
index 339a3e73846..2c000293f26 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs
@@ -18,6 +18,10 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
 use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
 
+mod inherent_projection;
+mod opaques;
+mod weak_types;
+
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn compute_projection_goal(
diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs
index f08adc0208b..ebd129f32b9 100644
--- a/compiler/rustc_trait_selection/src/solve/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs
@@ -7,7 +7,7 @@ use rustc_middle::traits::Reveal;
 use rustc_middle::ty;
 use rustc_middle::ty::util::NotUniqueParam;
 
-use super::{EvalCtxt, SolverMode};
+use crate::solve::{EvalCtxt, SolverMode};
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     pub(super) fn normalize_opaque_type(
diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs
index 54de32cf618..54de32cf618 100644
--- a/compiler/rustc_trait_selection/src/solve/weak_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals/weak_types.rs
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs
index 33513f6bd43..33513f6bd43 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index d9059e46a8c..cc1f13a2f0f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -1,6 +1,6 @@
 use super::{ObligationCauseCode, PredicateObligation};
 use crate::infer::error_reporting::TypeErrCtxt;
-use rustc_ast::{MetaItem, NestedMetaItem};
+use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{struct_span_err, ErrorGuaranteed};
@@ -474,18 +474,40 @@ impl<'tcx> OnUnimplementedDirective {
     }
 
     pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
-        let mut is_diagnostic_namespace_variant = false;
-        let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| {
-            if tcx.features().diagnostic_namespace {
-                is_diagnostic_namespace_variant = true;
-                tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next()
-            } else {
-                None
-            }
-        }) else {
-            return Ok(None);
-        };
+        if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
+            return Self::parse_attribute(attr, false, tcx, item_def_id);
+        } else if tcx.features().diagnostic_namespace {
+            tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
+                .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
+                .try_fold(None, |aggr: Option<Self>, directive| {
+                    let directive = directive?;
+                    if let Some(aggr) = aggr {
+                        let mut subcommands = aggr.subcommands;
+                        subcommands.extend(directive.subcommands);
+                        Ok(Some(Self {
+                            condition: aggr.condition.or(directive.condition),
+                            subcommands,
+                            message: aggr.message.or(directive.message),
+                            label: aggr.label.or(directive.label),
+                            note: aggr.note.or(directive.note),
+                            parent_label: aggr.parent_label.or(directive.parent_label),
+                            append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
+                        }))
+                    } else {
+                        Ok(Some(directive))
+                    }
+                })
+        } else {
+            Ok(None)
+        }
+    }
 
+    fn parse_attribute(
+        attr: &Attribute,
+        is_diagnostic_namespace_variant: bool,
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+    ) -> Result<Option<Self>, ErrorGuaranteed> {
         let result = if let Some(items) = attr.meta_item_list() {
             Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
         } else if let Some(value) = attr.value_str() {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 9d7933e23a8..a5ccf62608e 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -157,10 +157,18 @@ where
         }
 
         let mut region_constraints = QueryRegionConstraints::default();
-        let (output, error_info, mut obligations, _) =
-            Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| {
-                infcx.tcx.sess.delay_span_bug(span, format!("error performing {self:?}"))
-            })?;
+        let (output, error_info, mut obligations) =
+            Q::fully_perform_into(self, infcx, &mut region_constraints)
+                .map_err(|_| {
+                    infcx.tcx.sess.delay_span_bug(span, format!("error performing {self:?}"))
+                })
+                .and_then(|(output, error_info, obligations, certainty)| match certainty {
+                    Certainty::Proven => Ok((output, error_info, obligations)),
+                    Certainty::Ambiguous => Err(infcx
+                        .tcx
+                        .sess
+                        .delay_span_bug(span, format!("ambiguity performing {self:?}"))),
+                })?;
 
         // Typically, instantiating NLL query results does not
         // create obligations. However, in some cases there
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 147b600f7ba..1a9de150041 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -5,6 +5,9 @@
 //! This API is completely unstable and subject to change.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
+#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
+#![cfg_attr(not(bootstrap), allow(internal_features))]
 #![feature(assert_matches)]
 #![feature(iterator_try_collect)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs
index f36f4ec8697..f1037fe0baf 100644
--- a/compiler/rustc_type_ir/src/structural_impls.rs
+++ b/compiler/rustc_type_ir/src/structural_impls.rs
@@ -153,6 +153,12 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] {
     }
 }
 
+impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        self.iter().try_for_each(|t| t.visit_with(visitor))
+    }
+}
+
 impl<I: Interner, T: TypeFoldable<I>, Ix: Idx> TypeFoldable<I> for IndexVec<Ix, T> {
     fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
         self.try_map_id(|x| x.try_fold_with(folder))
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
index 104985493ef..a3b05f2435e 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -22,7 +22,8 @@ use std::fmt;
 use std::fmt::Debug;
 
 use self::ty::{
-    GenericPredicates, Generics, ImplDef, ImplTrait, Span, TraitDecl, TraitDef, Ty, TyKind,
+    GenericPredicates, Generics, ImplDef, ImplTrait, IndexedVal, LineInfo, Span, TraitDecl,
+    TraitDef, Ty, TyKind,
 };
 
 #[macro_use]
@@ -41,7 +42,7 @@ pub type CrateNum = usize;
 
 /// A unique identification number for each item accessible for the current compilation unit.
 #[derive(Clone, Copy, PartialEq, Eq)]
-pub struct DefId(pub usize);
+pub struct DefId(usize);
 
 impl Debug for DefId {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -52,9 +53,28 @@ impl Debug for DefId {
     }
 }
 
+impl IndexedVal for DefId {
+    fn to_val(index: usize) -> Self {
+        DefId(index)
+    }
+
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
+
 /// A unique identification number for each provenance
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub struct AllocId(pub usize);
+pub struct AllocId(usize);
+
+impl IndexedVal for AllocId {
+    fn to_val(index: usize) -> Self {
+        AllocId(index)
+    }
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
 
 /// A list of crate items.
 pub type CrateItems = Vec<CrateItem>;
@@ -88,6 +108,7 @@ pub struct Crate {
 }
 
 pub type DefKind = Opaque;
+pub type Filename = Opaque;
 
 /// Holds information about an item in the crate.
 /// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to
@@ -125,9 +146,9 @@ pub fn local_crate() -> Crate {
     with(|cx| cx.local_crate())
 }
 
-/// Try to find a crate with the given name.
-pub fn find_crate(name: &str) -> Option<Crate> {
-    with(|cx| cx.find_crate(name))
+/// Try to find a crate or crates if multiple crates exist from given name.
+pub fn find_crates(name: &str) -> Vec<Crate> {
+    with(|cx| cx.find_crates(name))
 }
 
 /// Try to find a crate with the given name.
@@ -174,15 +195,21 @@ pub trait Context {
     fn external_crates(&self) -> Vec<Crate>;
 
     /// Find a crate with the given name.
-    fn find_crate(&self, name: &str) -> Option<Crate>;
+    fn find_crates(&self, name: &str) -> Vec<Crate>;
 
-    /// Prints the name of given `DefId`
+    /// Returns the name of given `DefId`
     fn name_of_def_id(&self, def_id: DefId) -> String;
 
-    /// Prints a human readable form of `Span`
-    fn print_span(&self, span: Span) -> String;
+    /// Returns printable, human readable form of `Span`
+    fn span_to_string(&self, span: Span) -> String;
+
+    /// Return filename from given `Span`, for diagnostic purposes
+    fn get_filename(&self, span: &Span) -> Filename;
+
+    /// Return lines corresponding to this `Span`
+    fn get_lines(&self, span: &Span) -> LineInfo;
 
-    /// Prints the kind of given `DefId`
+    /// Returns the `kind` of given `DefId`
     fn def_kind(&mut self, def_id: DefId) -> DefKind;
 
     /// `Span` of an item
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index 6029e3c11ad..003045a4696 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -3,7 +3,7 @@ use super::{
     mir::{Body, Mutability},
     with, AllocId, DefId, Symbol,
 };
-use crate::Opaque;
+use crate::{Filename, Opaque};
 use std::fmt::{self, Debug, Formatter};
 
 #[derive(Copy, Clone)]
@@ -75,17 +75,48 @@ pub struct Placeholder<T> {
 }
 
 #[derive(Clone, Copy, PartialEq, Eq)]
-pub struct Span(pub usize);
+pub struct Span(usize);
 
 impl Debug for Span {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         f.debug_struct("Span")
             .field("id", &self.0)
-            .field("repr", &with(|cx| cx.print_span(*self)))
+            .field("repr", &with(|cx| cx.span_to_string(*self)))
             .finish()
     }
 }
 
+impl Span {
+    /// Return filename for diagnostic purposes
+    pub fn get_filename(&self) -> Filename {
+        with(|c| c.get_filename(self))
+    }
+
+    /// Return lines that corespond to this `Span`
+    pub fn get_lines(&self) -> LineInfo {
+        with(|c| c.get_lines(&self))
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+/// Information you get from `Span` in a struct form.
+/// Line and col start from 1.
+pub struct LineInfo {
+    pub start_line: usize,
+    pub start_col: usize,
+    pub end_line: usize,
+    pub end_col: usize,
+}
+
+impl IndexedVal for Span {
+    fn to_val(index: usize) -> Self {
+        Span(index)
+    }
+    fn to_index(&self) -> usize {
+        self.0
+    }
+}
+
 #[derive(Clone, Debug)]
 pub enum TyKind {
     RigidTy(RigidTy),
@@ -565,3 +596,9 @@ pub enum ImplPolarity {
     Negative,
     Reservation,
 }
+
+pub trait IndexedVal {
+    fn to_val(index: usize) -> Self;
+
+    fn to_index(&self) -> usize;
+}