about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2024-07-11 20:06:05 +0300
committerLaurențiu Nicola <lnicola@dend.ro>2024-07-11 20:06:05 +0300
commit62bbce2ad2b20d5cf1282da407d01de5c54161f1 (patch)
tree24923a8226aad3064a6c5739540c2d404d64f76e /compiler/rustc_codegen_ssa
parentb159b3fd80f7aac6aef57c64f820adbc11bc692c (diff)
parentbcf1f6db4594ae6132378b179a30cdb3599a863d (diff)
downloadrust-62bbce2ad2b20d5cf1282da407d01de5c54161f1.tar.gz
rust-62bbce2ad2b20d5cf1282da407d01de5c54161f1.zip
Merge from rust-lang/rust
Diffstat (limited to 'compiler/rustc_codegen_ssa')
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs124
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs742
-rw-r--r--compiler/rustc_codegen_ssa/src/back/rpath.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/rpath/tests.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs104
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/meth.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs87
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/locals.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs18
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/consts.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/misc.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/type_.rs6
21 files changed, 569 insertions, 587 deletions
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 1a851ad04a1..000fe2e3ce0 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -27,8 +27,6 @@ codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
 
 codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
 
-codegen_ssa_expected_coverage_symbol = expected `coverage(off)` or `coverage(on)`
-
 codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
 
 codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index c352100b01b..1f627353d54 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -45,7 +45,7 @@ use tempfile::Builder as TempFileBuilder;
 
 use itertools::Itertools;
 use std::collections::BTreeSet;
-use std::ffi::{OsStr, OsString};
+use std::ffi::OsString;
 use std::fs::{read, File, OpenOptions};
 use std::io::{BufWriter, Write};
 use std::ops::Deref;
@@ -1306,12 +1306,12 @@ fn link_sanitizer_runtime(
         let filename = format!("rustc{channel}_rt.{name}");
         let path = find_sanitizer_runtime(sess, &filename);
         let rpath = path.to_str().expect("non-utf8 component in path");
-        linker.args(&["-Wl,-rpath", "-Xlinker", rpath]);
+        linker.cc_args(&["-Wl,-rpath", "-Xlinker", rpath]);
         linker.link_dylib_by_name(&filename, false, true);
     } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" {
         // MSVC provides the `/INFERASANLIBS` argument to automatically find the
         // compatible ASAN library.
-        linker.arg("/INFERASANLIBS");
+        linker.link_arg("/INFERASANLIBS");
     } else {
         let filename = format!("librustc{channel}_rt.{name}.a");
         let path = find_sanitizer_runtime(sess, &filename).join(&filename);
@@ -1490,11 +1490,6 @@ fn print_native_static_libs(
     let mut lib_args: Vec<_> = all_native_libs
         .iter()
         .filter(|l| relevant_lib(sess, l))
-        // Deduplication of successive repeated libraries, see rust-lang/rust#113209
-        //
-        // note: we don't use PartialEq/Eq because NativeLib transitively depends on local
-        // elements like spans, which we don't care about and would make the deduplication impossible
-        .dedup_by(|l1, l2| l1.name == l2.name && l1.kind == l2.kind && l1.verbatim == l2.verbatim)
         .filter_map(|lib| {
             let name = lib.name;
             match lib.kind {
@@ -1521,6 +1516,8 @@ fn print_native_static_libs(
                 | NativeLibKind::RawDylib => None,
             }
         })
+        // deduplication of consecutive repeated libraries, see rust-lang/rust#113209
+        .dedup()
         .collect();
     for path in all_rust_dylibs {
         // FIXME deduplicate with add_dynamic_crate
@@ -1891,9 +1888,9 @@ fn add_post_link_objects(
 /// FIXME: Determine where exactly these args need to be inserted.
 fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
     if let Some(args) = sess.target.pre_link_args.get(&flavor) {
-        cmd.args(args.iter().map(Deref::deref));
+        cmd.verbatim_args(args.iter().map(Deref::deref));
     }
-    cmd.args(&sess.opts.unstable_opts.pre_link_args);
+    cmd.verbatim_args(&sess.opts.unstable_opts.pre_link_args);
 }
 
 /// Add a link script embedded in the target, if applicable.
@@ -1911,8 +1908,7 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty
                 sess.dcx().emit_fatal(errors::LinkScriptWriteFailure { path, error });
             }
 
-            cmd.arg("--script");
-            cmd.arg(path);
+            cmd.link_arg("--script").link_arg(path);
         }
         _ => {}
     }
@@ -1921,7 +1917,7 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty
 /// Add arbitrary "user defined" args defined from command line.
 /// FIXME: Determine where exactly these args need to be inserted.
 fn add_user_defined_link_args(cmd: &mut dyn Linker, sess: &Session) {
-    cmd.args(&sess.opts.cg.link_args);
+    cmd.verbatim_args(&sess.opts.cg.link_args);
 }
 
 /// Add arbitrary "late link" args defined by the target spec.
@@ -1939,15 +1935,15 @@ fn add_late_link_args(
         });
     if any_dynamic_crate {
         if let Some(args) = sess.target.late_link_args_dynamic.get(&flavor) {
-            cmd.args(args.iter().map(Deref::deref));
+            cmd.verbatim_args(args.iter().map(Deref::deref));
         }
     } else {
         if let Some(args) = sess.target.late_link_args_static.get(&flavor) {
-            cmd.args(args.iter().map(Deref::deref));
+            cmd.verbatim_args(args.iter().map(Deref::deref));
         }
     }
     if let Some(args) = sess.target.late_link_args.get(&flavor) {
-        cmd.args(args.iter().map(Deref::deref));
+        cmd.verbatim_args(args.iter().map(Deref::deref));
     }
 }
 
@@ -1955,7 +1951,7 @@ fn add_late_link_args(
 /// FIXME: Determine where exactly these args need to be inserted.
 fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
     if let Some(args) = sess.target.post_link_args.get(&flavor) {
-        cmd.args(args.iter().map(Deref::deref));
+        cmd.verbatim_args(args.iter().map(Deref::deref));
     }
 }
 
@@ -2100,6 +2096,10 @@ fn add_rpath_args(
     codegen_results: &CodegenResults,
     out_filename: &Path,
 ) {
+    if !sess.target.has_rpath {
+        return;
+    }
+
     // FIXME (#2397): At some point we want to rpath our guesses as to
     // where extern libraries might live, based on the
     // add_lib_search_paths
@@ -2118,11 +2118,10 @@ fn add_rpath_args(
         let rpath_config = RPathConfig {
             libs: &*libs,
             out_filename: out_filename.to_path_buf(),
-            has_rpath: sess.target.has_rpath,
             is_like_osx: sess.target.is_like_osx,
             linker_is_gnu: sess.target.linker_flavor.is_gnu(),
         };
-        cmd.args(&rpath::get_rpath_flags(&rpath_config));
+        cmd.cc_args(&rpath::get_rpath_flags(&rpath_config));
     }
 }
 
@@ -2381,7 +2380,7 @@ fn add_order_independent_options(
         } else {
             ""
         };
-        cmd.arg(format!("--dynamic-linker={prefix}ld.so.1"));
+        cmd.link_arg(format!("--dynamic-linker={prefix}ld.so.1"));
     }
 
     if sess.target.eh_frame_header {
@@ -2396,8 +2395,7 @@ fn add_order_independent_options(
     }
 
     if sess.target.os == "emscripten" {
-        cmd.arg("-s");
-        cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
+        cmd.cc_arg("-s").cc_arg(if sess.panic_strategy() == PanicStrategy::Abort {
             "DISABLE_EXCEPTION_CATCHING=1"
         } else {
             "DISABLE_EXCEPTION_CATCHING=0"
@@ -2405,22 +2403,21 @@ fn add_order_independent_options(
     }
 
     if flavor == LinkerFlavor::Llbc {
-        cmd.arg("--target");
-        cmd.arg(sess.target.llvm_target.as_ref());
-        cmd.arg("--target-cpu");
-        cmd.arg(&codegen_results.crate_info.target_cpu);
+        cmd.link_args(&[
+            "--target",
+            sess.target.llvm_target.as_ref(),
+            "--target-cpu",
+            &codegen_results.crate_info.target_cpu,
+        ]);
     } else if flavor == LinkerFlavor::Ptx {
-        cmd.arg("--fallback-arch");
-        cmd.arg(&codegen_results.crate_info.target_cpu);
+        cmd.link_args(&["--fallback-arch", &codegen_results.crate_info.target_cpu]);
     } else if flavor == LinkerFlavor::Bpf {
-        cmd.arg("--cpu");
-        cmd.arg(&codegen_results.crate_info.target_cpu);
+        cmd.link_args(&["--cpu", &codegen_results.crate_info.target_cpu]);
         if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features]
             .into_iter()
             .find(|feat| !feat.is_empty())
         {
-            cmd.arg("--cpu-features");
-            cmd.arg(feat);
+            cmd.link_args(&["--cpu-features", feat]);
         }
     }
 
@@ -2621,7 +2618,11 @@ fn add_native_libs_from_crate(
             NativeLibKind::WasmImportModule => {}
             NativeLibKind::LinkArg => {
                 if link_static {
-                    cmd.linker_arg(OsStr::new(name), verbatim);
+                    if verbatim {
+                        cmd.verbatim_arg(name);
+                    } else {
+                        cmd.link_arg(name);
+                    }
                 }
             }
         }
@@ -2816,6 +2817,15 @@ fn rehome_sysroot_lib_dir(sess: &Session, lib_dir: &Path) -> PathBuf {
     }
 }
 
+fn rehome_lib_path(sess: &Session, path: &Path) -> PathBuf {
+    if let Some(dir) = path.parent() {
+        let file_name = path.file_name().expect("library path has no file name component");
+        rehome_sysroot_lib_dir(sess, dir).join(file_name)
+    } else {
+        fix_windows_verbatim_for_gcc(path)
+    }
+}
+
 // Adds the static "rlib" versions of all crates to the command line.
 // There's a bit of magic which happens here specifically related to LTO,
 // namely that we remove upstream object files.
@@ -2846,15 +2856,8 @@ fn add_static_crate(
     let src = &codegen_results.crate_info.used_crate_source[&cnum];
     let cratepath = &src.rlib.as_ref().unwrap().0;
 
-    let mut link_upstream = |path: &Path| {
-        let rlib_path = if let Some(dir) = path.parent() {
-            let file_name = path.file_name().expect("rlib path has no file name path component");
-            rehome_sysroot_lib_dir(sess, dir).join(file_name)
-        } else {
-            fix_windows_verbatim_for_gcc(path)
-        };
-        cmd.link_staticlib_by_path(&rlib_path, false);
-    };
+    let mut link_upstream =
+        |path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false);
 
     if !are_upstream_rust_objects_already_included(sess)
         || ignored_for_lto(sess, &codegen_results.crate_info, cnum)
@@ -2918,27 +2921,7 @@ fn add_static_crate(
 
 // Same thing as above, but for dynamic crates instead of static crates.
 fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
-    // Just need to tell the linker about where the library lives and
-    // what its name is
-    let parent = cratepath.parent();
-    // When producing a dll, the MSVC linker may not actually emit a
-    // `foo.lib` file if the dll doesn't actually export any symbols, so we
-    // check to see if the file is there and just omit linking to it if it's
-    // not present.
-    if sess.target.is_like_msvc && !cratepath.with_extension("dll.lib").exists() {
-        return;
-    }
-    if let Some(dir) = parent {
-        cmd.include_path(&rehome_sysroot_lib_dir(sess, dir));
-    }
-    // "<dir>/name.dll -> name.dll" on windows-msvc
-    // "<dir>/name.dll -> name" on windows-gnu
-    // "<dir>/libname.<ext> -> name" elsewhere
-    let stem = if sess.target.is_like_msvc { cratepath.file_name() } else { cratepath.file_stem() };
-    let stem = stem.unwrap().to_str().unwrap();
-    // Convert library file-stem into a cc -l argument.
-    let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 };
-    cmd.link_dylib_by_name(&stem[prefix..], false, true);
+    cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true);
 }
 
 fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
@@ -3015,10 +2998,10 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
             // This is admittedly a bit strange, as on most targets
             // `-isysroot` only applies to include header files, but on Apple
             // targets this also applies to libraries and frameworks.
-            cmd.args(&["-isysroot", &sdk_root]);
+            cmd.cc_args(&["-isysroot", &sdk_root]);
         }
         LinkerFlavor::Darwin(Cc::No, _) => {
-            cmd.args(&["-syslibroot", &sdk_root]);
+            cmd.link_args(&["-syslibroot", &sdk_root]);
         }
         _ => unreachable!(),
     }
@@ -3029,8 +3012,9 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
         // search path.
 
         // The flags are called `-L` and `-F` both in Clang, ld64 and ldd.
-        cmd.arg(format!("-L{sdk_root}/System/iOSSupport/usr/lib"));
-        cmd.arg(format!("-F{sdk_root}/System/iOSSupport/System/Library/Frameworks"));
+        let sdk_root = Path::new(&sdk_root);
+        cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib"));
+        cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"));
     }
 }
 
@@ -3145,7 +3129,7 @@ fn add_lld_args(
         for path in sess.get_tools_search_paths(false) {
             let linker_path = path.join("gcc-ld");
             linker_path_exists |= linker_path.exists();
-            cmd.arg({
+            cmd.cc_arg({
                 let mut arg = OsString::from("-B");
                 arg.push(linker_path);
                 arg
@@ -3165,7 +3149,7 @@ fn add_lld_args(
     // is to use LLD but the `wasm32-wasip2` target relies on a wrapper around
     // this, `wasm-component-ld`, which is overridden if this option is passed.
     if !sess.target.is_like_wasm {
-        cmd.arg("-fuse-ld=lld");
+        cmd.cc_arg("-fuse-ld=lld");
     }
 
     if !flavor.is_gnu() {
@@ -3189,7 +3173,7 @@ fn add_lld_args(
         // targeting a different linker flavor on macOS, and that's also always
         // the case when targeting WASM.
         if sess.target.linker_flavor != sess.host.linker_flavor {
-            cmd.arg(format!("--target={}", sess.target.llvm_target));
+            cmd.cc_arg(format!("--target={}", sess.target.llvm_target));
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index a82478900b1..2bd5dfdce83 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -8,7 +8,7 @@ use std::fs::{self, File};
 use std::io::prelude::*;
 use std::io::{self, BufWriter};
 use std::path::{Path, PathBuf};
-use std::{env, mem, str};
+use std::{env, iter, mem, str};
 
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_metadata::find_native_static_library;
@@ -159,6 +159,102 @@ pub fn get_linker<'a>(
     }
 }
 
+// Note: Ideally neither these helper function, nor the macro-generated inherent methods below
+// would exist, and these functions would live in `trait Linker`.
+// Unfortunately, adding these functions to `trait Linker` make it `dyn`-incompatible.
+// If the methods are added to the trait with `where Self: Sized` bounds, then even a separate
+// implementation of them for `dyn Linker {}` wouldn't work due to a conflict with those
+// uncallable methods in the trait.
+
+/// Just pass the arguments to the linker as is.
+/// It is assumed that they are correctly prepared in advance.
+fn verbatim_args<L: Linker + ?Sized>(
+    l: &mut L,
+    args: impl IntoIterator<Item: AsRef<OsStr>>,
+) -> &mut L {
+    for arg in args {
+        l.cmd().arg(arg);
+    }
+    l
+}
+/// Arguments for the underlying linker.
+/// Add options to pass them through cc wrapper if `Linker` is a cc wrapper.
+fn link_args<L: Linker + ?Sized>(
+    l: &mut L,
+    args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>,
+) -> &mut L {
+    let args = args.into_iter();
+    if !l.is_cc() {
+        verbatim_args(l, args);
+    } else if args.len() != 0 {
+        // FIXME: Support arguments with commas, see `rpaths_to_flags` for the example.
+        let mut combined_arg = OsString::from("-Wl");
+        for arg in args {
+            combined_arg.push(",");
+            combined_arg.push(arg);
+        }
+        l.cmd().arg(combined_arg);
+    }
+    l
+}
+/// Arguments for the cc wrapper specifically.
+/// Check that it's indeed a cc wrapper and pass verbatim.
+fn cc_args<L: Linker + ?Sized>(l: &mut L, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut L {
+    assert!(l.is_cc());
+    verbatim_args(l, args)
+}
+/// Arguments supported by both underlying linker and cc wrapper, pass verbatim.
+fn link_or_cc_args<L: Linker + ?Sized>(
+    l: &mut L,
+    args: impl IntoIterator<Item: AsRef<OsStr>>,
+) -> &mut L {
+    verbatim_args(l, args)
+}
+
+macro_rules! generate_arg_methods {
+    ($($ty:ty)*) => { $(
+        impl $ty {
+            pub fn verbatim_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {
+                verbatim_args(self, args)
+            }
+            pub fn verbatim_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                verbatim_args(self, iter::once(arg))
+            }
+            pub fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>) -> &mut Self {
+                link_args(self, args)
+            }
+            pub fn link_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                link_args(self, iter::once(arg))
+            }
+            pub fn cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {
+                cc_args(self, args)
+            }
+            pub fn cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                cc_args(self, iter::once(arg))
+            }
+            pub fn link_or_cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {
+                link_or_cc_args(self, args)
+            }
+            pub fn link_or_cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                link_or_cc_args(self, iter::once(arg))
+            }
+        }
+    )* }
+}
+
+generate_arg_methods! {
+    GccLinker<'_>
+    MsvcLinker<'_>
+    EmLinker<'_>
+    WasmLd<'_>
+    L4Bender<'_>
+    AixLinker<'_>
+    LlbcLinker<'_>
+    PtxLinker<'_>
+    BpfLinker<'_>
+    dyn Linker + '_
+}
+
 /// Linker abstraction used by `back::link` to build up the command to invoke a
 /// linker.
 ///
@@ -168,17 +264,33 @@ pub fn get_linker<'a>(
 /// MSVC linker (e.g., `link.exe`) is being used.
 pub trait Linker {
     fn cmd(&mut self) -> &mut Command;
+    fn is_cc(&self) -> bool {
+        false
+    }
     fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path);
-    fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool);
+    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
+        bug!("dylib linked with unsupported linker")
+    }
+    fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool) {
+        bug!("dylib linked with unsupported linker")
+    }
     fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
         bug!("framework linked with unsupported linker")
     }
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool);
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool);
-    fn include_path(&mut self, path: &Path);
-    fn framework_path(&mut self, path: &Path);
-    fn output_filename(&mut self, path: &Path);
-    fn add_object(&mut self, path: &Path);
+    fn include_path(&mut self, path: &Path) {
+        link_or_cc_args(link_or_cc_args(self, &["-L"]), &[path]);
+    }
+    fn framework_path(&mut self, _path: &Path) {
+        bug!("framework path set with unsupported linker")
+    }
+    fn output_filename(&mut self, path: &Path) {
+        link_or_cc_args(link_or_cc_args(self, &["-o"]), &[path]);
+    }
+    fn add_object(&mut self, path: &Path) {
+        link_or_cc_args(self, &[path]);
+    }
     fn gc_sections(&mut self, keep_metadata: bool);
     fn no_gc_sections(&mut self);
     fn full_relro(&mut self);
@@ -198,25 +310,9 @@ pub trait Linker {
     fn add_no_exec(&mut self) {}
     fn add_as_needed(&mut self) {}
     fn reset_per_library_state(&mut self) {}
-    fn linker_arg(&mut self, arg: &OsStr, verbatim: bool) {
-        self.linker_args(&[arg], verbatim);
-    }
-    fn linker_args(&mut self, args: &[&OsStr], _verbatim: bool) {
-        args.into_iter().for_each(|a| {
-            self.cmd().arg(a);
-        });
-    }
 }
 
 impl dyn Linker + '_ {
-    pub fn arg(&mut self, arg: impl AsRef<OsStr>) {
-        self.cmd().arg(arg);
-    }
-
-    pub fn args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) {
-        self.cmd().args(args);
-    }
-
     pub fn take_cmd(&mut self) -> Command {
         mem::replace(self.cmd(), Command::new(""))
     }
@@ -233,14 +329,6 @@ pub struct GccLinker<'a> {
 }
 
 impl<'a> GccLinker<'a> {
-    fn linker_arg(&mut self, arg: impl AsRef<OsStr>) {
-        Linker::linker_arg(self, arg.as_ref(), false);
-    }
-    fn linker_args(&mut self, args: &[impl AsRef<OsStr>]) {
-        let args_vec: Vec<&OsStr> = args.iter().map(|x| x.as_ref()).collect();
-        Linker::linker_args(self, &args_vec, false);
-    }
-
     fn takes_hints(&self) -> bool {
         // Really this function only returns true if the underlying linker
         // configured for a compiler is binutils `ld.bfd` and `ld.gold`. We
@@ -262,7 +350,7 @@ impl<'a> GccLinker<'a> {
             return;
         }
         if self.hinted_static != Some(true) {
-            self.linker_arg("-Bstatic");
+            self.link_arg("-Bstatic");
             self.hinted_static = Some(true);
         }
     }
@@ -272,7 +360,7 @@ impl<'a> GccLinker<'a> {
             return;
         }
         if self.hinted_static != Some(false) {
-            self.linker_arg("-Bdynamic");
+            self.link_arg("-Bdynamic");
             self.hinted_static = Some(false);
         }
     }
@@ -281,7 +369,7 @@ impl<'a> GccLinker<'a> {
         if let Some(plugin_path) = plugin_path {
             let mut arg = OsString::from("-plugin=");
             arg.push(plugin_path);
-            self.linker_arg(&arg);
+            self.link_arg(&arg);
         }
 
         let opt_level = match self.sess.opts.optimize {
@@ -292,9 +380,9 @@ impl<'a> GccLinker<'a> {
         };
 
         if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use {
-            self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
+            self.link_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
         };
-        self.linker_args(&[
+        self.link_args(&[
             &format!("-plugin-opt={opt_level}"),
             &format!("-plugin-opt=mcpu={}", self.target_cpu),
         ]);
@@ -304,10 +392,10 @@ impl<'a> GccLinker<'a> {
         // On mac we need to tell the linker to let this library be rpathed
         if self.sess.target.is_like_osx {
             if !self.is_ld {
-                self.cmd.arg("-dynamiclib");
+                self.cc_arg("-dynamiclib");
             }
 
-            self.linker_arg("-dylib");
+            self.link_arg("-dylib");
 
             // Note that the `osx_rpath_install_name` option here is a hack
             // purely to support rustbuild right now, we should get a more
@@ -316,105 +404,110 @@ impl<'a> GccLinker<'a> {
             if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name {
                 let mut rpath = OsString::from("@rpath/");
                 rpath.push(out_filename.file_name().unwrap());
-                self.linker_args(&[OsString::from("-install_name"), rpath]);
+                self.link_arg("-install_name").link_arg(rpath);
             }
         } else {
-            self.cmd.arg("-shared");
-            if self.sess.target.is_like_windows {
-                // The output filename already contains `dll_suffix` so
-                // the resulting import library will have a name in the
-                // form of libfoo.dll.a
-                let implib_name =
-                    out_filename.file_name().and_then(|file| file.to_str()).map(|file| {
-                        format!(
-                            "{}{}{}",
-                            self.sess.target.staticlib_prefix,
-                            file,
-                            self.sess.target.staticlib_suffix
-                        )
-                    });
-                if let Some(implib_name) = implib_name {
-                    let implib = out_filename.parent().map(|dir| dir.join(&implib_name));
-                    if let Some(implib) = implib {
-                        self.linker_arg(&format!("--out-implib={}", (*implib).to_str().unwrap()));
-                    }
+            self.link_or_cc_arg("-shared");
+            if let Some(name) = out_filename.file_name() {
+                if self.sess.target.is_like_windows {
+                    // The output filename already contains `dll_suffix` so
+                    // the resulting import library will have a name in the
+                    // form of libfoo.dll.a
+                    let mut implib_name = OsString::from(&*self.sess.target.staticlib_prefix);
+                    implib_name.push(name);
+                    implib_name.push(&*self.sess.target.staticlib_suffix);
+                    let mut out_implib = OsString::from("--out-implib=");
+                    out_implib.push(out_filename.with_file_name(implib_name));
+                    self.link_arg(out_implib);
+                } else {
+                    // When dylibs are linked by a full path this value will get into `DT_NEEDED`
+                    // instead of the full path, so the library can be later found in some other
+                    // location than that specific path.
+                    let mut soname = OsString::from("-soname=");
+                    soname.push(name);
+                    self.link_arg(soname);
                 }
             }
         }
     }
-}
 
-impl<'a> Linker for GccLinker<'a> {
-    /// Passes a series of arguments directly to the linker.
-    ///
-    /// When the linker is ld-like, the arguments are simply appended to the command. When the
-    /// linker is not ld-like such as when using a compiler as a linker, the arguments are joined by
-    /// commas to form an argument that is then prepended with `-Wl`. In this situation, only a
-    /// single argument is appended to the command to ensure that the order of the arguments is
-    /// preserved by the compiler.
-    fn linker_args(&mut self, args: &[&OsStr], verbatim: bool) {
-        if self.is_ld || verbatim {
-            args.into_iter().for_each(|a| {
-                self.cmd.arg(a);
-            });
-        } else {
-            if !args.is_empty() {
-                let mut s = OsString::from("-Wl");
-                for a in args {
-                    s.push(",");
-                    s.push(a);
-                }
-                self.cmd.arg(s);
+    fn with_as_needed(&mut self, as_needed: bool, f: impl FnOnce(&mut Self)) {
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // FIXME(81490): ld64 doesn't support these flags but macOS 11
+                // has -needed-l{} / -needed_library {}
+                // but we have no way to detect that here.
+                self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
+            } else if self.is_gnu && !self.sess.target.is_like_windows {
+                self.link_arg("--no-as-needed");
+            } else {
+                self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier);
+            }
+        }
+
+        f(self);
+
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // See above FIXME comment
+            } else if self.is_gnu && !self.sess.target.is_like_windows {
+                self.link_arg("--as-needed");
             }
         }
     }
+}
 
+impl<'a> Linker for GccLinker<'a> {
     fn cmd(&mut self) -> &mut Command {
         &mut self.cmd
     }
 
+    fn is_cc(&self) -> bool {
+        !self.is_ld
+    }
+
     fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) {
         match output_kind {
             LinkOutputKind::DynamicNoPicExe => {
                 if !self.is_ld && self.is_gnu {
-                    self.cmd.arg("-no-pie");
+                    self.cc_arg("-no-pie");
                 }
             }
             LinkOutputKind::DynamicPicExe => {
                 // noop on windows w/ gcc & ld, error w/ lld
                 if !self.sess.target.is_like_windows {
                     // `-pie` works for both gcc wrapper and ld.
-                    self.cmd.arg("-pie");
+                    self.link_or_cc_arg("-pie");
                 }
             }
             LinkOutputKind::StaticNoPicExe => {
                 // `-static` works for both gcc wrapper and ld.
-                self.cmd.arg("-static");
+                self.link_or_cc_arg("-static");
                 if !self.is_ld && self.is_gnu {
-                    self.cmd.arg("-no-pie");
+                    self.cc_arg("-no-pie");
                 }
             }
             LinkOutputKind::StaticPicExe => {
                 if !self.is_ld {
                     // Note that combination `-static -pie` doesn't work as expected
                     // for the gcc wrapper, `-static` in that case suppresses `-pie`.
-                    self.cmd.arg("-static-pie");
+                    self.cc_arg("-static-pie");
                 } else {
                     // `--no-dynamic-linker` and `-z text` are not strictly necessary for producing
                     // a static pie, but currently passed because gcc and clang pass them.
                     // The former suppresses the `INTERP` ELF header specifying dynamic linker,
                     // which is otherwise implicitly injected by ld (but not lld).
                     // The latter doesn't change anything, only ensures that everything is pic.
-                    self.cmd.args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]);
+                    self.link_args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]);
                 }
             }
             LinkOutputKind::DynamicDylib => self.build_dylib(out_filename),
             LinkOutputKind::StaticDylib => {
-                self.cmd.arg("-static");
+                self.link_or_cc_arg("-static");
                 self.build_dylib(out_filename);
             }
             LinkOutputKind::WasiReactorExe => {
-                self.linker_args(&["--entry", "_initialize"]);
+                self.link_args(&["--entry", "_initialize"]);
             }
         }
         // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc,
@@ -430,7 +523,7 @@ impl<'a> Linker for GccLinker<'a> {
                     | LinkOutputKind::StaticDylib
             )
         {
-            self.cmd.arg("--static-crt");
+            self.cc_arg("--static-crt");
         }
     }
 
@@ -443,27 +536,18 @@ impl<'a> Linker for GccLinker<'a> {
             // to the linker.
             return;
         }
-        if !as_needed {
-            if self.sess.target.is_like_osx {
-                // FIXME(81490): ld64 doesn't support these flags but macOS 11
-                // has -needed-l{} / -needed_library {}
-                // but we have no way to detect that here.
-                self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
-            } else if self.is_gnu && !self.sess.target.is_like_windows {
-                self.linker_arg("--no-as-needed");
-            } else {
-                self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier);
-            }
-        }
         self.hint_dynamic();
-        self.cmd.arg(format!("-l{}{name}", if verbatim && self.is_gnu { ":" } else { "" },));
-        if !as_needed {
-            if self.sess.target.is_like_osx {
-                // See above FIXME comment
-            } else if self.is_gnu && !self.sess.target.is_like_windows {
-                self.linker_arg("--as-needed");
-            }
-        }
+        self.with_as_needed(as_needed, |this| {
+            let colon = if verbatim && this.is_gnu { ":" } else { "" };
+            this.link_or_cc_arg(format!("-l{colon}{name}"));
+        });
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool) {
+        self.hint_dynamic();
+        self.with_as_needed(as_needed, |this| {
+            this.link_or_cc_arg(path);
+        })
     }
 
     fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool) {
@@ -471,63 +555,51 @@ impl<'a> Linker for GccLinker<'a> {
         if !as_needed {
             // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework
             // flag but we have no way to detect that here.
-            // self.cmd.arg("-needed_framework").arg(name);
+            // self.link_or_cc_arg("-needed_framework").link_or_cc_arg(name);
             self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
         }
-        self.cmd.arg("-framework").arg(name);
+        self.link_or_cc_args(&["-framework", name]);
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
         self.hint_static();
         let colon = if verbatim && self.is_gnu { ":" } else { "" };
         if !whole_archive {
-            self.cmd.arg(format!("-l{colon}{name}"));
+            self.link_or_cc_arg(format!("-l{colon}{name}"));
         } else if self.sess.target.is_like_osx {
             // -force_load is the macOS equivalent of --whole-archive, but it
             // involves passing the full path to the library to link.
-            self.linker_arg("-force_load");
-            self.linker_arg(find_native_static_library(name, verbatim, self.sess));
+            self.link_arg("-force_load");
+            self.link_arg(find_native_static_library(name, verbatim, self.sess));
         } else {
-            self.linker_arg("--whole-archive");
-            self.cmd.arg(format!("-l{colon}{name}"));
-            self.linker_arg("--no-whole-archive");
+            self.link_arg("--whole-archive")
+                .link_or_cc_arg(format!("-l{colon}{name}"))
+                .link_arg("--no-whole-archive");
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else if self.sess.target.is_like_osx {
-            self.linker_arg("-force_load");
-            self.linker_arg(path);
+            self.link_arg("-force_load").link_arg(path);
         } else {
-            self.linker_arg("--whole-archive");
-            self.linker_arg(path);
-            self.linker_arg("--no-whole-archive");
+            self.link_arg("--whole-archive").link_arg(path).link_arg("--no-whole-archive");
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
     fn framework_path(&mut self, path: &Path) {
-        self.cmd.arg("-F").arg(path);
-    }
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+        self.link_or_cc_arg("-F").link_or_cc_arg(path);
     }
     fn full_relro(&mut self) {
-        self.linker_args(&["-z", "relro", "-z", "now"]);
+        self.link_args(&["-z", "relro", "-z", "now"]);
     }
     fn partial_relro(&mut self) {
-        self.linker_args(&["-z", "relro"]);
+        self.link_args(&["-z", "relro"]);
     }
     fn no_relro(&mut self) {
-        self.linker_args(&["-z", "norelro"]);
+        self.link_args(&["-z", "norelro"]);
     }
 
     fn gc_sections(&mut self, keep_metadata: bool) {
@@ -546,7 +618,7 @@ impl<'a> Linker for GccLinker<'a> {
         // for partial linking when using multiple codegen units (-r). So we
         // insert it here.
         if self.sess.target.is_like_osx {
-            self.linker_arg("-dead_strip");
+            self.link_arg("-dead_strip");
 
         // If we're building a dylib, we don't use --gc-sections because LLVM
         // has already done the best it can do, and we also don't want to
@@ -554,13 +626,13 @@ impl<'a> Linker for GccLinker<'a> {
         // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67%
         // reduction.
         } else if (self.is_gnu || self.sess.target.is_like_wasm) && !keep_metadata {
-            self.linker_arg("--gc-sections");
+            self.link_arg("--gc-sections");
         }
     }
 
     fn no_gc_sections(&mut self) {
         if self.is_gnu || self.sess.target.is_like_wasm {
-            self.linker_arg("--no-gc-sections");
+            self.link_arg("--no-gc-sections");
         }
     }
 
@@ -574,7 +646,7 @@ impl<'a> Linker for GccLinker<'a> {
         if self.sess.opts.optimize == config::OptLevel::Default
             || self.sess.opts.optimize == config::OptLevel::Aggressive
         {
-            self.linker_arg("-O1");
+            self.link_arg("-O1");
         }
     }
 
@@ -594,8 +666,7 @@ impl<'a> Linker for GccLinker<'a> {
         //
         // Though it may be worth to try to revert those changes upstream, since
         // the overhead of the initialization should be minor.
-        self.cmd.arg("-u");
-        self.cmd.arg("__llvm_profile_runtime");
+        self.link_or_cc_args(&["-u", "__llvm_profile_runtime"]);
     }
 
     fn control_flow_guard(&mut self) {}
@@ -616,33 +687,33 @@ impl<'a> Linker for GccLinker<'a> {
                 // The --strip-debug case is handled by running an external
                 // `strip` utility as a separate step after linking.
                 if !self.sess.target.is_like_solaris {
-                    self.linker_arg("--strip-debug");
+                    self.link_arg("--strip-debug");
                 }
             }
             Strip::Symbols => {
-                self.linker_arg("--strip-all");
+                self.link_arg("--strip-all");
             }
         }
         match self.sess.opts.unstable_opts.debuginfo_compression {
             config::DebugInfoCompression::None => {}
             config::DebugInfoCompression::Zlib => {
-                self.linker_arg("--compress-debug-sections=zlib");
+                self.link_arg("--compress-debug-sections=zlib");
             }
             config::DebugInfoCompression::Zstd => {
-                self.linker_arg("--compress-debug-sections=zstd");
+                self.link_arg("--compress-debug-sections=zstd");
             }
         }
     }
 
     fn no_crt_objects(&mut self) {
         if !self.is_ld {
-            self.cmd.arg("-nostartfiles");
+            self.cc_arg("-nostartfiles");
         }
     }
 
     fn no_default_libraries(&mut self) {
         if !self.is_ld {
-            self.cmd.arg("-nodefaultlibs");
+            self.cc_arg("-nodefaultlibs");
         }
     }
 
@@ -718,24 +789,22 @@ impl<'a> Linker for GccLinker<'a> {
         }
 
         if self.sess.target.is_like_osx {
-            self.linker_args(&[OsString::from("-exported_symbols_list"), path.into()]);
+            self.link_arg("-exported_symbols_list").link_arg(path);
         } else if self.sess.target.is_like_solaris {
-            self.linker_args(&[OsString::from("-M"), path.into()]);
+            self.link_arg("-M").link_arg(path);
         } else {
             if is_windows {
-                self.linker_arg(path);
+                self.link_arg(path);
             } else {
                 let mut arg = OsString::from("--version-script=");
                 arg.push(path);
-                self.linker_arg(arg);
-                self.linker_arg("--no-undefined-version");
+                self.link_arg(arg).link_arg("--no-undefined-version");
             }
         }
     }
 
     fn subsystem(&mut self, subsystem: &str) {
-        self.linker_arg("--subsystem");
-        self.linker_arg(&subsystem);
+        self.link_args(&["--subsystem", subsystem]);
     }
 
     fn reset_per_library_state(&mut self) {
@@ -760,23 +829,23 @@ impl<'a> Linker for GccLinker<'a> {
     // Some versions of `gcc` add it implicitly, some (e.g. `musl-gcc`) don't,
     // so we just always add it.
     fn add_eh_frame_header(&mut self) {
-        self.linker_arg("--eh-frame-hdr");
+        self.link_arg("--eh-frame-hdr");
     }
 
     fn add_no_exec(&mut self) {
         if self.sess.target.is_like_windows {
-            self.linker_arg("--nxcompat");
+            self.link_arg("--nxcompat");
         } else if self.is_gnu {
-            self.linker_args(&["-z", "noexecstack"]);
+            self.link_args(&["-z", "noexecstack"]);
         }
     }
 
     fn add_as_needed(&mut self) {
         if self.is_gnu && !self.sess.target.is_like_windows {
-            self.linker_arg("--as-needed");
+            self.link_arg("--as-needed");
         } else if self.sess.target.is_like_solaris {
             // -z ignore is the Solaris equivalent to the GNU ld --as-needed option
-            self.linker_args(&["-z", "ignore"]);
+            self.link_args(&["-z", "ignore"]);
         }
     }
 }
@@ -798,10 +867,10 @@ impl<'a> Linker for MsvcLinker<'a> {
             | LinkOutputKind::StaticNoPicExe
             | LinkOutputKind::StaticPicExe => {}
             LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
-                self.cmd.arg("/DLL");
+                self.link_arg("/DLL");
                 let mut arg: OsString = "/IMPLIB:".into();
                 arg.push(out_filename.with_extension("dll.lib"));
-                self.cmd.arg(arg);
+                self.link_arg(arg);
             }
             LinkOutputKind::WasiReactorExe => {
                 panic!("can't link as reactor on non-wasi target");
@@ -810,44 +879,49 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, _as_needed: bool) {
-        self.cmd.arg(format!("{}{}", name, if verbatim { "" } else { ".lib" }));
+        self.link_arg(format!("{}{}", name, if verbatim { "" } else { ".lib" }));
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        // When producing a dll, MSVC linker may not emit an implib file if the dll doesn't export
+        // any symbols, so we skip linking if the implib file is not present.
+        let implib_path = path.with_extension("dll.lib");
+        if implib_path.exists() {
+            self.link_or_cc_arg(implib_path);
+        }
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
         let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" };
         let suffix = if verbatim { "" } else { ".lib" };
-        self.cmd.arg(format!("{prefix}{name}{suffix}"));
+        self.link_arg(format!("{prefix}{name}{suffix}"));
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_arg(path);
         } else {
             let mut arg = OsString::from("/WHOLEARCHIVE:");
             arg.push(path);
-            self.cmd.arg(arg);
+            self.link_arg(arg);
         }
     }
 
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn gc_sections(&mut self, _keep_metadata: bool) {
         // MSVC's ICF (Identical COMDAT Folding) link optimization is
         // slow for Rust and thus we disable it by default when not in
         // optimization build.
         if self.sess.opts.optimize != config::OptLevel::No {
-            self.cmd.arg("/OPT:REF,ICF");
+            self.link_arg("/OPT:REF,ICF");
         } else {
             // It is necessary to specify NOICF here, because /OPT:REF
             // implies ICF by default.
-            self.cmd.arg("/OPT:REF,NOICF");
+            self.link_arg("/OPT:REF,NOICF");
         }
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("/OPT:NOREF,NOICF");
+        self.link_arg("/OPT:NOREF,NOICF");
     }
 
     fn full_relro(&mut self) {
@@ -867,23 +941,19 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn no_default_libraries(&mut self) {
-        self.cmd.arg("/NODEFAULTLIB");
+        self.link_arg("/NODEFAULTLIB");
     }
 
     fn include_path(&mut self, path: &Path) {
         let mut arg = OsString::from("/LIBPATH:");
         arg.push(path);
-        self.cmd.arg(&arg);
+        self.link_arg(&arg);
     }
 
     fn output_filename(&mut self, path: &Path) {
         let mut arg = OsString::from("/OUT:");
         arg.push(path);
-        self.cmd.arg(&arg);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        bug!("frameworks are not supported on windows")
+        self.link_arg(&arg);
     }
 
     fn optimize(&mut self) {
@@ -895,19 +965,19 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn control_flow_guard(&mut self) {
-        self.cmd.arg("/guard:cf");
+        self.link_arg("/guard:cf");
     }
 
     fn ehcont_guard(&mut self) {
         if self.sess.target.pointer_width == 64 {
-            self.cmd.arg("/guard:ehcont");
+            self.link_arg("/guard:ehcont");
         }
     }
 
     fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
         // This will cause the Microsoft linker to generate a PDB file
         // from the CodeView line tables in the object files.
-        self.cmd.arg("/DEBUG");
+        self.link_arg("/DEBUG");
 
         // Default to emitting only the file name of the PDB file into
         // the binary instead of the full path. Emitting the full path
@@ -916,7 +986,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         //
         // This default behavior can be overridden by explicitly passing
         // `-Clink-arg=/PDBALTPATH:...` to rustc.
-        self.cmd.arg("/PDBALTPATH:%_PDB%");
+        self.link_arg("/PDBALTPATH:%_PDB%");
 
         // This will cause the Microsoft linker to embed .natvis info into the PDB file
         let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
@@ -928,7 +998,7 @@ impl<'a> Linker for MsvcLinker<'a> {
                         if path.extension() == Some("natvis".as_ref()) {
                             let mut arg = OsString::from("/NATVIS:");
                             arg.push(path);
-                            self.cmd.arg(arg);
+                            self.link_arg(arg);
                         }
                     }
                     Err(error) => {
@@ -942,7 +1012,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         for path in natvis_debugger_visualizers {
             let mut arg = OsString::from("/NATVIS:");
             arg.push(path);
-            self.cmd.arg(arg);
+            self.link_arg(arg);
         }
     }
 
@@ -986,13 +1056,13 @@ impl<'a> Linker for MsvcLinker<'a> {
         }
         let mut arg = OsString::from("/DEF:");
         arg.push(path);
-        self.cmd.arg(&arg);
+        self.link_arg(&arg);
     }
 
     fn subsystem(&mut self, subsystem: &str) {
         // Note that previous passes of the compiler validated this subsystem,
         // so we just blindly pass it to the linker.
-        self.cmd.arg(&format!("/SUBSYSTEM:{subsystem}"));
+        self.link_arg(&format!("/SUBSYSTEM:{subsystem}"));
 
         // Windows has two subsystems we're interested in right now, the console
         // and windows subsystems. These both implicitly have different entry
@@ -1009,7 +1079,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         //
         // For more information see RFC #1665
         if subsystem == "windows" {
-            self.cmd.arg("/ENTRY:mainCRTStartup");
+            self.link_arg("/ENTRY:mainCRTStartup");
         }
     }
 
@@ -1018,7 +1088,7 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn add_no_exec(&mut self) {
-        self.cmd.arg("/NXCOMPAT");
+        self.link_arg("/NXCOMPAT");
     }
 }
 
@@ -1032,31 +1102,27 @@ impl<'a> Linker for EmLinker<'a> {
         &mut self.cmd
     }
 
+    fn is_cc(&self) -> bool {
+        true
+    }
+
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
     fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
         // Emscripten always links statically
-        self.cmd.arg("-l").arg(name);
+        self.link_or_cc_args(&["-l", name]);
     }
 
-    fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) {
-        self.cmd.arg("-l").arg(name);
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        self.link_or_cc_arg(path);
     }
 
-    fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
+    fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) {
+        self.link_or_cc_args(&["-l", name]);
     }
 
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+    fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
+        self.link_or_cc_arg(path);
     }
 
     fn full_relro(&mut self) {
@@ -1071,10 +1137,6 @@ impl<'a> Linker for EmLinker<'a> {
         // noop
     }
 
-    fn framework_path(&mut self, _path: &Path) {
-        bug!("frameworks are not supported on Emscripten")
-    }
-
     fn gc_sections(&mut self, _keep_metadata: bool) {
         // noop
     }
@@ -1085,7 +1147,7 @@ impl<'a> Linker for EmLinker<'a> {
 
     fn optimize(&mut self) {
         // Emscripten performs own optimizations
-        self.cmd.arg(match self.sess.opts.optimize {
+        self.cc_arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
             OptLevel::Less => "-O1",
             OptLevel::Default => "-O2",
@@ -1106,7 +1168,7 @@ impl<'a> Linker for EmLinker<'a> {
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         // Preserve names or generate source maps depending on debug info
         // For more information see https://emscripten.org/docs/tools_reference/emcc.html#emcc-g
-        self.cmd.arg(match self.sess.opts.debuginfo {
+        self.cc_arg(match self.sess.opts.debuginfo {
             DebugInfo::None => "-g0",
             DebugInfo::Limited | DebugInfo::LineTablesOnly | DebugInfo::LineDirectivesOnly => {
                 "--profiling-funcs"
@@ -1118,13 +1180,13 @@ impl<'a> Linker for EmLinker<'a> {
     fn no_crt_objects(&mut self) {}
 
     fn no_default_libraries(&mut self) {
-        self.cmd.arg("-nodefaultlibs");
+        self.cc_arg("-nodefaultlibs");
     }
 
     fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
         debug!("EXPORTED SYMBOLS:");
 
-        self.cmd.arg("-s");
+        self.cc_arg("-s");
 
         let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
         let encoded = serde_json::to_string(
@@ -1135,7 +1197,7 @@ impl<'a> Linker for EmLinker<'a> {
 
         arg.push(encoded);
 
-        self.cmd.arg(arg);
+        self.cc_arg(arg);
     }
 
     fn subsystem(&mut self, _subsystem: &str) {
@@ -1153,7 +1215,7 @@ pub struct WasmLd<'a> {
 }
 
 impl<'a> WasmLd<'a> {
-    fn new(mut cmd: Command, sess: &'a Session) -> WasmLd<'a> {
+    fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> {
         // If the atomics feature is enabled for wasm then we need a whole bunch
         // of flags:
         //
@@ -1172,18 +1234,19 @@ impl<'a> WasmLd<'a> {
         // On wasm32-unknown-unknown, we also export symbols for glue code to use:
         //    * `--export=*tls*` - when `#[thread_local]` symbols are used these
         //      symbols are how the TLS segments are initialized and configured.
+        let mut wasm_ld = WasmLd { cmd, sess };
         if sess.target_features.contains(&sym::atomics) {
-            cmd.arg("--shared-memory");
-            cmd.arg("--max-memory=1073741824");
-            cmd.arg("--import-memory");
+            wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]);
             if sess.target.os == "unknown" {
-                cmd.arg("--export=__wasm_init_tls");
-                cmd.arg("--export=__tls_size");
-                cmd.arg("--export=__tls_align");
-                cmd.arg("--export=__tls_base");
+                wasm_ld.link_args(&[
+                    "--export=__wasm_init_tls",
+                    "--export=__tls_size",
+                    "--export=__tls_align",
+                    "--export=__tls_base",
+                ]);
             }
         }
-        WasmLd { cmd, sess }
+        wasm_ld
     }
 }
 
@@ -1199,51 +1262,40 @@ impl<'a> Linker for WasmLd<'a> {
             | LinkOutputKind::StaticNoPicExe
             | LinkOutputKind::StaticPicExe => {}
             LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
-                self.cmd.arg("--no-entry");
+                self.link_arg("--no-entry");
             }
             LinkOutputKind::WasiReactorExe => {
-                self.cmd.arg("--entry");
-                self.cmd.arg("_initialize");
+                self.link_args(&["--entry", "_initialize"]);
             }
         }
     }
 
     fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
-        self.cmd.arg("-l").arg(name);
+        self.link_or_cc_args(&["-l", name]);
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        self.link_or_cc_arg(path);
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
         if !whole_archive {
-            self.cmd.arg("-l").arg(name);
+            self.link_or_cc_args(&["-l", name]);
         } else {
-            self.cmd.arg("--whole-archive").arg("-l").arg(name).arg("--no-whole-archive");
+            self.link_arg("--whole-archive")
+                .link_or_cc_args(&["-l", name])
+                .link_arg("--no-whole-archive");
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else {
-            self.cmd.arg("--whole-archive").arg(path).arg("--no-whole-archive");
+            self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive");
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1251,17 +1303,17 @@ impl<'a> Linker for WasmLd<'a> {
     fn no_relro(&mut self) {}
 
     fn gc_sections(&mut self, _keep_metadata: bool) {
-        self.cmd.arg("--gc-sections");
+        self.link_arg("--gc-sections");
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("--no-gc-sections");
+        self.link_arg("--no-gc-sections");
     }
 
     fn optimize(&mut self) {
         // The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and
         // only differentiates -O0 and -O1. It does not apply to LTO.
-        self.cmd.arg(match self.sess.opts.optimize {
+        self.link_arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
             OptLevel::Less => "-O1",
             OptLevel::Default => "-O2",
@@ -1279,10 +1331,10 @@ impl<'a> Linker for WasmLd<'a> {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
-                self.cmd.arg("--strip-debug");
+                self.link_arg("--strip-debug");
             }
             Strip::Symbols => {
-                self.cmd.arg("--strip-all");
+                self.link_arg("--strip-all");
             }
         }
     }
@@ -1297,7 +1349,7 @@ impl<'a> Linker for WasmLd<'a> {
 
     fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
         for sym in symbols {
-            self.cmd.arg("--export").arg(&sym);
+            self.link_args(&["--export", sym]);
         }
 
         // LLD will hide these otherwise-internal symbols since it only exports
@@ -1305,8 +1357,7 @@ impl<'a> Linker for WasmLd<'a> {
         // others. Various bits and pieces of wasm32-unknown-unknown tooling use
         // this, so be sure these symbols make their way out of the linker as well.
         if self.sess.target.os == "unknown" {
-            self.cmd.arg("--export=__heap_base");
-            self.cmd.arg("--export=__data_end");
+            self.link_args(&["--export=__heap_base", "--export=__data_end"]);
         }
     }
 
@@ -1337,7 +1388,7 @@ impl<'a> WasmLd<'a> {
             // wasm-ld only handles integer LTO opt levels. Use O2
             config::OptLevel::Size | config::OptLevel::SizeMin => "O2",
         };
-        self.cmd.arg(&format!("--lto-{opt_level}"));
+        self.link_arg(&format!("--lto-{opt_level}"));
     }
 }
 
@@ -1355,63 +1406,46 @@ impl<'a> Linker for L4Bender<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        bug!("dylibs are not supported on L4Re");
-    }
-
     fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(format!("-PC{name}"));
+            self.link_arg(format!("-PC{name}"));
         } else {
-            self.cmd.arg("--whole-archive").arg(format!("-l{name}")).arg("--no-whole-archive");
+            self.link_arg("--whole-archive")
+                .link_or_cc_arg(format!("-l{name}"))
+                .link_arg("--no-whole-archive");
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else {
-            self.cmd.arg("--whole-archive").arg(path).arg("--no-whole-archive");
+            self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive");
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-    fn framework_path(&mut self, _: &Path) {
-        bug!("frameworks are not supported on L4Re");
-    }
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn full_relro(&mut self) {
-        self.cmd.arg("-z").arg("relro");
-        self.cmd.arg("-z").arg("now");
+        self.link_args(&["-z", "relro", "-z", "now"]);
     }
 
     fn partial_relro(&mut self) {
-        self.cmd.arg("-z").arg("relro");
+        self.link_args(&["-z", "relro"]);
     }
 
     fn no_relro(&mut self) {
-        self.cmd.arg("-z").arg("norelro");
+        self.link_args(&["-z", "norelro"]);
     }
 
     fn gc_sections(&mut self, keep_metadata: bool) {
         if !keep_metadata {
-            self.cmd.arg("--gc-sections");
+            self.link_arg("--gc-sections");
         }
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("--no-gc-sections");
+        self.link_arg("--no-gc-sections");
     }
 
     fn optimize(&mut self) {
@@ -1420,7 +1454,7 @@ impl<'a> Linker for L4Bender<'a> {
         if self.sess.opts.optimize == config::OptLevel::Default
             || self.sess.opts.optimize == config::OptLevel::Aggressive
         {
-            self.cmd.arg("-O1");
+            self.link_arg("-O1");
         }
     }
 
@@ -1430,16 +1464,16 @@ impl<'a> Linker for L4Bender<'a> {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
-                self.cmd().arg("--strip-debug");
+                self.link_arg("--strip-debug");
             }
             Strip::Symbols => {
-                self.cmd().arg("--strip-all");
+                self.link_arg("--strip-all");
             }
         }
     }
 
     fn no_default_libraries(&mut self) {
-        self.cmd.arg("-nostdlib");
+        self.cc_arg("-nostdlib");
     }
 
     fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) {
@@ -1449,7 +1483,7 @@ impl<'a> Linker for L4Bender<'a> {
     }
 
     fn subsystem(&mut self, subsystem: &str) {
-        self.cmd.arg(&format!("--subsystem {subsystem}"));
+        self.link_arg(&format!("--subsystem {subsystem}"));
     }
 
     fn reset_per_library_state(&mut self) {
@@ -1467,12 +1501,12 @@ impl<'a> Linker for L4Bender<'a> {
 
 impl<'a> L4Bender<'a> {
     pub fn new(cmd: Command, sess: &'a Session) -> L4Bender<'a> {
-        L4Bender { cmd: cmd, sess: sess, hinted_static: false }
+        L4Bender { cmd, sess: sess, hinted_static: false }
     }
 
     fn hint_static(&mut self) {
         if !self.hinted_static {
-            self.cmd.arg("-static");
+            self.link_or_cc_arg("-static");
             self.hinted_static = true;
         }
     }
@@ -1487,29 +1521,28 @@ pub struct AixLinker<'a> {
 
 impl<'a> AixLinker<'a> {
     pub fn new(cmd: Command, sess: &'a Session) -> AixLinker<'a> {
-        AixLinker { cmd: cmd, sess: sess, hinted_static: None }
+        AixLinker { cmd, sess: sess, hinted_static: None }
     }
 
     fn hint_static(&mut self) {
         if self.hinted_static != Some(true) {
-            self.cmd.arg("-bstatic");
+            self.link_arg("-bstatic");
             self.hinted_static = Some(true);
         }
     }
 
     fn hint_dynamic(&mut self) {
         if self.hinted_static != Some(false) {
-            self.cmd.arg("-bdynamic");
+            self.link_arg("-bdynamic");
             self.hinted_static = Some(false);
         }
     }
 
     fn build_dylib(&mut self, _out_filename: &Path) {
-        self.cmd.arg("-bM:SRE");
-        self.cmd.arg("-bnoentry");
+        self.link_args(&["-bM:SRE", "-bnoentry"]);
         // FIXME: Use CreateExportList utility to create export list
         // and remove -bexpfull.
-        self.cmd.arg("-bexpfull");
+        self.link_arg("-bexpfull");
     }
 }
 
@@ -1534,47 +1567,36 @@ impl<'a> Linker for AixLinker<'a> {
 
     fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
         self.hint_dynamic();
-        self.cmd.arg(format!("-l{name}"));
+        self.link_or_cc_arg(format!("-l{name}"));
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        self.hint_dynamic();
+        self.link_or_cc_arg(path);
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(format!("-l{name}"));
+            self.link_or_cc_arg(format!("-l{name}"));
         } else {
             let mut arg = OsString::from("-bkeepfile:");
             arg.push(find_native_static_library(name, verbatim, self.sess));
-            self.cmd.arg(arg);
+            self.link_or_cc_arg(arg);
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else {
             let mut arg = OsString::from("-bkeepfile:");
             arg.push(path);
-            self.cmd.arg(arg);
+            self.link_arg(arg);
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-
-    fn framework_path(&mut self, _: &Path) {
-        bug!("frameworks are not supported on AIX");
-    }
-
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1582,17 +1604,17 @@ impl<'a> Linker for AixLinker<'a> {
     fn no_relro(&mut self) {}
 
     fn gc_sections(&mut self, _keep_metadata: bool) {
-        self.cmd.arg("-bgc");
+        self.link_arg("-bgc");
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("-bnogc");
+        self.link_arg("-bnogc");
     }
 
     fn optimize(&mut self) {}
 
     fn pgo_gen(&mut self) {
-        self.cmd.arg("-bdbg:namedsects:ss");
+        self.link_arg("-bdbg:namedsects:ss");
     }
 
     fn control_flow_guard(&mut self) {}
@@ -1618,7 +1640,7 @@ impl<'a> Linker for AixLinker<'a> {
         if let Err(e) = res {
             self.sess.dcx().fatal(format!("failed to write export file: {e}"));
         }
-        self.cmd.arg(format!("-bE:{}", path.to_str().unwrap()));
+        self.link_arg(format!("-bE:{}", path.to_str().unwrap()));
     }
 
     fn subsystem(&mut self, _subsystem: &str) {}
@@ -1738,48 +1760,32 @@ impl<'a> Linker for PtxLinker<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        panic!("external dylibs not supported")
-    }
-
     fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
         panic!("staticlibs not supported")
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg("--rlib").arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
+        self.link_arg("--rlib").link_arg(path);
     }
 
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
-        self.cmd.arg("--debug");
+        self.link_arg("--debug");
     }
 
     fn add_object(&mut self, path: &Path) {
-        self.cmd.arg("--bitcode").arg(path);
+        self.link_arg("--bitcode").link_arg(path);
     }
 
     fn optimize(&mut self) {
         match self.sess.lto() {
             Lto::Thin | Lto::Fat | Lto::ThinLocal => {
-                self.cmd.arg("-Olto");
+                self.link_arg("-Olto");
             }
 
             Lto::No => {}
         }
     }
 
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1820,28 +1826,16 @@ impl<'a> Linker for LlbcLinker<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        panic!("external dylibs not supported")
-    }
-
     fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
         panic!("staticlibs not supported")
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
+        self.link_or_cc_arg(path);
     }
 
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
-        self.cmd.arg("--debug");
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+        self.link_arg("--debug");
     }
 
     fn optimize(&mut self) {
@@ -1855,14 +1849,6 @@ impl<'a> Linker for LlbcLinker<'a> {
         };
     }
 
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1887,7 +1873,7 @@ impl<'a> Linker for LlbcLinker<'a> {
         match _crate_type {
             CrateType::Cdylib => {
                 for sym in symbols {
-                    self.cmd.arg("--export-symbol").arg(sym);
+                    self.link_args(&["--export-symbol", sym]);
                 }
             }
             _ => (),
@@ -1911,32 +1897,20 @@ impl<'a> Linker for BpfLinker<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        panic!("external dylibs not supported")
-    }
-
     fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
         panic!("staticlibs not supported")
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
+        self.link_or_cc_arg(path);
     }
 
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
-        self.cmd.arg("--debug");
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+        self.link_arg("--debug");
     }
 
     fn optimize(&mut self) {
-        self.cmd.arg(match self.sess.opts.optimize {
+        self.link_arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
             OptLevel::Less => "-O1",
             OptLevel::Default => "-O2",
@@ -1946,14 +1920,6 @@ impl<'a> Linker for BpfLinker<'a> {
         });
     }
 
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1985,7 +1951,7 @@ impl<'a> Linker for BpfLinker<'a> {
         if let Err(error) = res {
             self.sess.dcx().emit_fatal(errors::SymbolFileWriteFailure { error });
         } else {
-            self.cmd.arg("--export-symbols").arg(&path);
+            self.link_arg("--export-symbols").link_arg(&path);
         }
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs
index f499bbcf853..82070d4453b 100644
--- a/compiler/rustc_codegen_ssa/src/back/rpath.rs
+++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs
@@ -9,16 +9,10 @@ pub struct RPathConfig<'a> {
     pub libs: &'a [&'a Path],
     pub out_filename: PathBuf,
     pub is_like_osx: bool,
-    pub has_rpath: bool,
     pub linker_is_gnu: bool,
 }
 
 pub fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec<OsString> {
-    // No rpath on windows
-    if !config.has_rpath {
-        return Vec::new();
-    }
-
     debug!("preparing the RPATH!");
 
     let rpaths = get_rpaths(config);
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
index 0de90a1036e..c620e92db1f 100644
--- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
+++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
@@ -37,7 +37,6 @@ fn test_rpath_relative() {
     if cfg!(target_os = "macos") {
         let config = &mut RPathConfig {
             libs: &[],
-            has_rpath: true,
             is_like_osx: true,
             linker_is_gnu: false,
             out_filename: PathBuf::from("bin/rustc"),
@@ -48,7 +47,6 @@ fn test_rpath_relative() {
         let config = &mut RPathConfig {
             libs: &[],
             out_filename: PathBuf::from("bin/rustc"),
-            has_rpath: true,
             is_like_osx: false,
             linker_is_gnu: true,
         };
@@ -62,7 +60,6 @@ fn test_rpath_relative_issue_119571() {
     let config = &mut RPathConfig {
         libs: &[],
         out_filename: PathBuf::from("rustc"),
-        has_rpath: true,
         is_like_osx: false,
         linker_is_gnu: true,
     };
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 064be4988bd..0e48eee3dd5 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -120,7 +120,6 @@ pub struct ModuleConfig {
     pub vectorize_loop: bool,
     pub vectorize_slp: bool,
     pub merge_functions: bool,
-    pub inline_threshold: Option<u32>,
     pub emit_lifetime_markers: bool,
     pub llvm_plugins: Vec<String>,
 }
@@ -280,7 +279,6 @@ impl ModuleConfig {
                 }
             },
 
-            inline_threshold: sess.opts.cg.inline_threshold,
             emit_lifetime_markers: sess.emit_lifetime_markers(),
             llvm_plugins: if_regular!(sess.opts.unstable_opts.llvm_plugins.clone(), vec![]),
         }
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index c18816533a2..137f14fe706 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -37,7 +37,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
 use rustc_session::Session;
 use rustc_span::symbol::sym;
-use rustc_span::Symbol;
+use rustc_span::{Symbol, DUMMY_SP};
 use rustc_target::abi::FIRST_VARIANT;
 
 use std::cmp;
@@ -467,6 +467,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 ty::ParamEnv::reveal_all(),
                 start_def_id,
                 cx.tcx().mk_args(&[main_ret_ty.into()]),
+                DUMMY_SP,
             );
             let start_fn = cx.get_fn_addr(start_instance);
 
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 15955170e87..56a893738df 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -1,11 +1,13 @@
 use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
 use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
-use rustc_errors::{codes::*, struct_span_code_err};
+use rustc_errors::{codes::*, struct_span_code_err, DiagMessage, SubdiagMessage};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
 use rustc_hir::{lang_items, weak_lang_items::WEAK_LANG_ITEMS, LangItem};
-use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
+use rustc_middle::middle::codegen_fn_attrs::{
+    CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
+};
 use rustc_middle::mir::mono::Linkage;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self as ty, TyCtxt};
@@ -15,11 +17,7 @@ use rustc_span::{sym, Span};
 use rustc_target::spec::{abi, SanitizerSet};
 
 use crate::errors;
-use crate::target_features::from_target_feature;
-use crate::{
-    errors::{ExpectedCoverageSymbol, ExpectedUsedSymbol},
-    target_features::check_target_feature_trait_unsafe,
-};
+use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
 
 fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
     use rustc_middle::mir::mono::Linkage::*;
@@ -128,21 +126,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         .emit();
                 }
             }
-            sym::coverage => {
-                let inner = attr.meta_item_list();
-                match inner.as_deref() {
-                    Some([item]) if item.has_name(sym::off) => {
-                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
-                    }
-                    Some([item]) if item.has_name(sym::on) => {
-                        // Allow #[coverage(on)] for being explicit, maybe also in future to enable
-                        // coverage on a smaller scope within an excluded larger scope.
-                    }
-                    Some(_) | None => {
-                        tcx.dcx().emit_err(ExpectedCoverageSymbol { span: attr.span });
-                    }
-                }
-            }
             sym::rustc_std_internal_symbol => {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
             }
@@ -174,7 +157,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
                     }
                     Some(_) => {
-                        tcx.dcx().emit_err(ExpectedUsedSymbol { span: attr.span });
+                        tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span });
                     }
                     None => {
                         // Unfortunately, unconditionally using `llvm.used` causes
@@ -466,6 +449,80 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     None
                 };
             }
+            sym::patchable_function_entry => {
+                codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
+                    let mut prefix = None;
+                    let mut entry = None;
+                    for item in l {
+                        let Some(meta_item) = item.meta_item() else {
+                            tcx.dcx().span_err(item.span(), "expected name value pair");
+                            continue;
+                        };
+
+                        let Some(name_value_lit) = meta_item.name_value_literal() else {
+                            tcx.dcx().span_err(item.span(), "expected name value pair");
+                            continue;
+                        };
+
+                        fn emit_error_with_label(
+                            tcx: TyCtxt<'_>,
+                            span: Span,
+                            error: impl Into<DiagMessage>,
+                            label: impl Into<SubdiagMessage>,
+                        ) {
+                            let mut err: rustc_errors::Diag<'_, _> =
+                                tcx.dcx().struct_span_err(span, error);
+                            err.span_label(span, label);
+                            err.emit();
+                        }
+
+                        let attrib_to_write = match meta_item.name_or_empty() {
+                            sym::prefix_nops => &mut prefix,
+                            sym::entry_nops => &mut entry,
+                            _ => {
+                                emit_error_with_label(
+                                    tcx,
+                                    item.span(),
+                                    "unexpected parameter name",
+                                    format!("expected {} or {}", sym::prefix_nops, sym::entry_nops),
+                                );
+                                continue;
+                            }
+                        };
+
+                        let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
+                            emit_error_with_label(
+                                tcx,
+                                name_value_lit.span,
+                                "invalid literal value",
+                                "value must be an integer between `0` and `255`",
+                            );
+                            continue;
+                        };
+
+                        let Ok(val) = val.get().try_into() else {
+                            emit_error_with_label(
+                                tcx,
+                                name_value_lit.span,
+                                "integer value out of range",
+                                "value must be between `0` and `255`",
+                            );
+                            continue;
+                        };
+
+                        *attrib_to_write = Some(val);
+                    }
+
+                    if let (None, None) = (prefix, entry) {
+                        tcx.dcx().span_err(attr.span, "must specify at least one parameter");
+                    }
+
+                    Some(PatchableFunctionEntry::from_prefix_and_entry(
+                        prefix.unwrap_or(0),
+                        entry.unwrap_or(0),
+                    ))
+                })
+            }
             _ => {}
         }
     }
@@ -587,7 +644,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     }
 
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
-        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
         codegen_fn_attrs.inline = InlineAttr::Never;
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index e6ba31c5165..e9d31db9254 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -565,13 +565,6 @@ pub struct UnknownArchiveKind<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_expected_coverage_symbol)]
-pub struct ExpectedCoverageSymbol {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_expected_used_symbol)]
 pub struct ExpectedUsedSymbol {
     #[primary_span]
diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs
index ddc6797388e..febc8ee2be2 100644
--- a/compiler/rustc_codegen_ssa/src/meth.rs
+++ b/compiler/rustc_codegen_ssa/src/meth.rs
@@ -133,6 +133,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
     let align = cx.data_layout().pointer_align.abi;
     let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
 
+    cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable);
     cx.create_vtable_debuginfo(ty, trait_ref, vtable);
     cx.vtables().borrow_mut().insert((ty, trait_ref), vtable);
     vtable
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 0577ba32ffd..ac2b6ca4e95 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -281,6 +281,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
                 | TerminatorKind::UnwindResume
                 | TerminatorKind::UnwindTerminate(_)
                 | TerminatorKind::Return
+                | TerminatorKind::TailCall { .. }
                 | TerminatorKind::CoroutineDrop
                 | TerminatorKind::Unreachable
                 | TerminatorKind::SwitchInt { .. }
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 57138d3b9db..6a5525dc2b3 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -403,7 +403,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             //
             // Why only in unoptimized builds?
             // - In unoptimized builds LLVM uses FastISel which does not support switches, so it
-            //   must fall back to the to the slower SelectionDAG isel. Therefore, using `br` gives
+            //   must fall back to the slower SelectionDAG isel. Therefore, using `br` gives
             //   significant compile time speedups for unoptimized builds.
             // - In optimized builds the above doesn't hold, and using `br` sometimes results in
             //   worse generated code because LLVM can no longer tell that the value being switched
@@ -658,7 +658,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // with #[rustc_inherit_overflow_checks] and inlined from
         // another crate (mostly core::num generic/#[inline] fns),
         // while the current crate doesn't use overflow checks.
-        if !bx.cx().check_overflow() && msg.is_optional_overflow_check() {
+        if !bx.sess().overflow_checks() && msg.is_optional_overflow_check() {
             const_cond = Some(expected);
         }
 
@@ -751,7 +751,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         &mut self,
         helper: &TerminatorCodegenHelper<'tcx>,
         bx: &mut Bx,
-        intrinsic: Option<ty::IntrinsicDef>,
+        intrinsic: ty::IntrinsicDef,
         instance: Option<Instance<'tcx>>,
         source_info: mir::SourceInfo,
         target: Option<mir::BasicBlock>,
@@ -761,8 +761,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // Emit a panic or a no-op for `assert_*` intrinsics.
         // These are intrinsics that compile to panics so that we can get a message
         // which mentions the offending type, even from a const context.
-        let panic_intrinsic = intrinsic.and_then(|i| ValidityRequirement::from_intrinsic(i.name));
-        if let Some(requirement) = panic_intrinsic {
+        if let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) {
             let ty = instance.unwrap().args.type_at(0);
 
             let do_panic = !bx
@@ -843,6 +842,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         ty::ParamEnv::reveal_all(),
                         def_id,
                         args,
+                        fn_span,
                     )
                     .polymorphize(bx.tcx()),
                 ),
@@ -869,12 +869,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         let sig = callee.layout.ty.fn_sig(bx.tcx());
         let abi = sig.abi();
 
-        // Handle intrinsics old codegen wants Expr's for, ourselves.
-        let intrinsic = match def {
-            Some(ty::InstanceKind::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()),
-            _ => None,
-        };
-
         let extra_args = &args[sig.inputs().skip_binder().len()..];
         let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| {
             let op_ty = op_arg.node.ty(self.mir, bx.tcx());
@@ -886,50 +880,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             None => bx.fn_abi_of_fn_ptr(sig, extra_args),
         };
 
-        if let Some(merging_succ) = self.codegen_panic_intrinsic(
-            &helper,
-            bx,
-            intrinsic,
-            instance,
-            source_info,
-            target,
-            unwind,
-            mergeable_succ,
-        ) {
-            return merging_succ;
-        }
-
         // The arguments we'll be passing. Plus one to account for outptr, if used.
         let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
 
-        if matches!(intrinsic, Some(ty::IntrinsicDef { name: sym::caller_location, .. })) {
-            return if let Some(target) = target {
-                let location =
-                    self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
-
-                let mut llargs = Vec::with_capacity(arg_count);
-                let ret_dest = self.make_return_dest(
+        let instance = match def {
+            Some(ty::InstanceKind::Intrinsic(def_id)) => {
+                let intrinsic = bx.tcx().intrinsic(def_id).unwrap();
+                if let Some(merging_succ) = self.codegen_panic_intrinsic(
+                    &helper,
                     bx,
-                    destination,
-                    &fn_abi.ret,
-                    &mut llargs,
                     intrinsic,
-                    Some(target),
-                );
-                assert_eq!(llargs, []);
-                if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
-                    location.val.store(bx, tmp);
+                    instance,
+                    source_info,
+                    target,
+                    unwind,
+                    mergeable_succ,
+                ) {
+                    return merging_succ;
                 }
-                self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate());
-                helper.funclet_br(self, bx, target, mergeable_succ)
-            } else {
-                MergingSucc::False
-            };
-        }
 
-        let instance = match intrinsic {
-            None => instance,
-            Some(intrinsic) => {
                 let mut llargs = Vec::with_capacity(1);
                 let ret_dest = self.make_return_dest(
                     bx,
@@ -971,6 +940,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     })
                     .collect();
 
+                if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) {
+                    let location = self
+                        .get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
+
+                    assert_eq!(llargs, []);
+                    if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
+                        location.val.store(bx, tmp);
+                    }
+                    self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate());
+                    return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ);
+                }
+
                 let instance = *instance.as_ref().unwrap();
                 match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) {
                     Ok(()) => {
@@ -997,6 +978,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                 }
             }
+            _ => instance,
         };
 
         let mut llargs = Vec::with_capacity(arg_count);
@@ -1407,6 +1389,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 fn_span,
                 mergeable_succ(),
             ),
+            mir::TerminatorKind::TailCall { .. } => {
+                // FIXME(explicit_tail_calls): implement tail calls in ssa backend
+                span_bug!(
+                    terminator.source_info.span,
+                    "`TailCall` terminator is not yet supported by `rustc_codegen_ssa`"
+                )
+            }
             mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => {
                 bug!("coroutine ops in codegen")
             }
@@ -1540,7 +1529,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 //   when passed by value, making it smaller.
                 // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes
                 //   when passed by value, making it larger.
-                let copy_bytes = cmp::min(scratch_size.bytes(), arg.layout.size.bytes());
+                let copy_bytes = cmp::min(cast.unaligned_size(bx).bytes(), arg.layout.size.bytes());
                 // Allocate some scratch space...
                 let llscratch = bx.alloca(scratch_size, scratch_align);
                 bx.lifetime_start(llscratch, scratch_size);
diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs
index c7f63eab829..5190021c005 100644
--- a/compiler/rustc_codegen_ssa/src/mir/locals.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs
@@ -1,6 +1,7 @@
 //! Locals are in a private module as updating `LocalRef::Operand` has to
 //! be careful wrt to subtyping. To deal with this we only allow updates by using
 //! `FunctionCx::overwrite_local` which handles it automatically.
+
 use crate::mir::{FunctionCx, LocalRef};
 use crate::traits::BuilderMethods;
 use rustc_index::IndexVec;
@@ -46,7 +47,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
                     if expected_ty != op.layout.ty {
                         warn!(
-                            "Unexpected initial operand type: expected {expected_ty:?}, found {:?}.\
+                            "Unexpected initial operand type:\nexpected {expected_ty:?},\nfound    {:?}.\n\
                             See <https://github.com/rust-lang/rust/issues/114858>.",
                             op.layout.ty
                         );
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index e8da9842882..61f57c9030a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -230,10 +230,20 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
             let layout = start_bx.layout_of(fx.monomorphize(decl.ty));
             assert!(!layout.ty.has_erasable_regions());
 
-            if local == mir::RETURN_PLACE && fx.fn_abi.ret.is_indirect() {
-                debug!("alloc: {:?} (return place) -> place", local);
-                let llretptr = start_bx.get_param(0);
-                return LocalRef::Place(PlaceRef::new_sized(llretptr, layout));
+            if local == mir::RETURN_PLACE {
+                match fx.fn_abi.ret.mode {
+                    PassMode::Indirect { .. } => {
+                        debug!("alloc: {:?} (return place) -> place", local);
+                        let llretptr = start_bx.get_param(0);
+                        return LocalRef::Place(PlaceRef::new_sized(llretptr, layout));
+                    }
+                    PassMode::Cast { ref cast, .. } => {
+                        debug!("alloc: {:?} (return place) -> place", local);
+                        let size = cast.size(&start_bx);
+                        return LocalRef::Place(PlaceRef::alloca_size(&mut start_bx, size, layout));
+                    }
+                    _ => {}
+                };
             }
 
             if memory_locals.contains(local) {
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 449fd9ae0db..97d5bb83128 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -109,8 +109,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         bx: &mut Bx,
         layout: TyAndLayout<'tcx>,
     ) -> Self {
+        Self::alloca_size(bx, layout.size, layout)
+    }
+
+    pub fn alloca_size<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
+        bx: &mut Bx,
+        size: Size,
+        layout: TyAndLayout<'tcx>,
+    ) -> Self {
         assert!(layout.is_sized(), "tried to statically allocate unsized place");
-        PlaceValue::alloca(bx, layout.size, layout.align.abi).with_type(layout)
+        PlaceValue::alloca(bx, size, layout.align.abi).with_type(layout)
     }
 
     /// Returns a place for an indirect reference to an unsized place.
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 05861e33767..f9e928a84a7 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -639,7 +639,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         (OperandValue::Immediate(llval), operand.layout)
                     }
                     mir::UnOp::PtrMetadata => {
-                        debug_assert!(operand.layout.ty.is_unsafe_ptr());
+                        debug_assert!(
+                            operand.layout.ty.is_unsafe_ptr() || operand.layout.ty.is_ref(),
+                        );
                         let (_, meta) = operand.val.pointer_parts();
                         assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
                         if let Some(meta) = meta {
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index e8b9490d401..3770bd11cf9 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -22,8 +22,6 @@ use rustc_session::{
 use rustc_span::symbol::Symbol;
 use rustc_target::abi::call::FnAbi;
 
-use std::fmt;
-
 pub trait BackendTypes {
     type Value: CodegenObject;
     type Function: CodegenObject;
@@ -62,7 +60,7 @@ pub trait CodegenBackend {
     fn locale_resource(&self) -> &'static str;
 
     fn init(&self, _sess: &Session) {}
-    fn print(&self, _req: &PrintRequest, _out: &mut dyn PrintBackendInfo, _sess: &Session) {}
+    fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {}
     fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> {
         vec![]
     }
@@ -150,19 +148,3 @@ pub trait ExtraBackendMethods:
         std::thread::Builder::new().name(name).spawn(f)
     }
 }
-
-pub trait PrintBackendInfo {
-    fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>);
-}
-
-impl PrintBackendInfo for String {
-    fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>) {
-        fmt::Write::write_fmt(self, args).unwrap();
-    }
-}
-
-impl dyn PrintBackendInfo + '_ {
-    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) {
-        self.infallible_write_fmt(args);
-    }
-}
diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs
index 8cb17a5b37a..3da732602c5 100644
--- a/compiler/rustc_codegen_ssa/src/traits/consts.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs
@@ -37,6 +37,5 @@ pub trait ConstMethods<'tcx>: BackendTypes {
 
     fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value;
 
-    fn const_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
     fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value;
 }
diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs
index 04e2b8796c4..0ace28ed3ba 100644
--- a/compiler/rustc_codegen_ssa/src/traits/misc.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs
@@ -9,7 +9,13 @@ pub trait MiscMethods<'tcx>: BackendTypes {
     fn vtables(
         &self,
     ) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), Self::Value>>;
-    fn check_overflow(&self) -> bool;
+    fn apply_vcall_visibility_metadata(
+        &self,
+        _ty: Ty<'tcx>,
+        _poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
+        _vtable: Self::Value,
+    ) {
+    }
     fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function;
     fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value;
     fn eh_personality(&self) -> Self::Value;
diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 728c2bc8c49..8cb58bd4c70 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -30,9 +30,7 @@ mod write;
 
 pub use self::abi::AbiBuilderMethods;
 pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
-pub use self::backend::{
-    Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo,
-};
+pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods};
 pub use self::builder::{BuilderMethods, OverflowOp};
 pub use self::consts::ConstMethods;
 pub use self::coverageinfo::CoverageInfoBuilderMethods;
diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs
index 403f6a73277..b1bad6cfa6f 100644
--- a/compiler/rustc_codegen_ssa/src/traits/type_.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs
@@ -12,7 +12,6 @@ use rustc_target::abi::{AddressSpace, Float, Integer};
 // This depends on `Backend` and not `BackendTypes`, because consumers will probably want to use
 // `LayoutOf` or `HasTyCtxt`. This way, they don't have to add a constraint on it themselves.
 pub trait BaseTypeMethods<'tcx>: Backend<'tcx> {
-    fn type_i1(&self) -> Self::Type;
     fn type_i8(&self) -> Self::Type;
     fn type_i16(&self) -> Self::Type;
     fn type_i32(&self) -> Self::Type;
@@ -27,7 +26,6 @@ pub trait BaseTypeMethods<'tcx>: Backend<'tcx> {
 
     fn type_array(&self, ty: Self::Type, len: u64) -> Self::Type;
     fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::Type;
-    fn type_struct(&self, els: &[Self::Type], packed: bool) -> Self::Type;
     fn type_kind(&self, ty: Self::Type) -> TypeKind;
     fn type_ptr(&self) -> Self::Type;
     fn type_ptr_ext(&self, address_space: AddressSpace) -> Self::Type;
@@ -115,8 +113,8 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
     /// The backend type used for a rust type when it's in an SSA register.
     ///
     /// For nearly all types this is the same as the [`Self::backend_type`], however
-    /// `bool` (and other `0`-or-`1` values) are kept as [`BaseTypeMethods::type_i1`]
-    /// in registers but as [`BaseTypeMethods::type_i8`] in memory.
+    /// `bool` (and other `0`-or-`1` values) are kept as `i1` in registers but as
+    /// [`BaseTypeMethods::type_i8`] in memory.
     ///
     /// Converting values between the two different backend types is done using
     /// [`from_immediate`](super::BuilderMethods::from_immediate) and