about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-11 16:27:23 +0000
committerbors <bors@rust-lang.org>2024-10-11 16:27:23 +0000
commit01e2fff90c7ed19e1d9fb828ebc012e7b9732297 (patch)
tree49b5869e859d2415e6f0a6b02f5bb1dbf1581d8d
parentf4966590d8edd5f493a1aab04016b94a75494329 (diff)
parentb18e1aa612d75114ee67091f3af82ea04a08ef36 (diff)
downloadrust-01e2fff90c7ed19e1d9fb828ebc012e7b9732297.tar.gz
rust-01e2fff90c7ed19e1d9fb828ebc012e7b9732297.zip
Auto merge of #131547 - matthiaskrgr:rollup-ui4p744, r=matthiaskrgr
Rollup of 6 pull requests

Successful merges:

 - #129079 (Create `_imp__` symbols also when doing ThinLTO)
 - #131208 (ABI: Pass aggregates by value on AIX)
 - #131394 (fix(rustdoc): add space between struct fields and their descriptions)
 - #131519 (Use Default visibility for rustc-generated C symbol declarations)
 - #131541 (compiletest: Extract auxiliary-crate properties to their own module/struct)
 - #131542 (next-solver: remove outdated FIXMEs)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_codegen_llvm/src/declare.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs8
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs11
-rw-r--r--compiler/rustc_target/src/abi/call/powerpc64.rs16
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs2
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css3
-rw-r--r--src/tools/compiletest/src/header.rs71
-rw-r--r--src/tools/compiletest/src/header/auxiliary.rs60
-rw-r--r--src/tools/compiletest/src/header/tests.rs3
-rw-r--r--src/tools/compiletest/src/lib.rs3
-rw-r--r--src/tools/compiletest/src/runtest.rs18
-rw-r--r--tests/assembly/powerpc64-struct-abi.rs43
-rw-r--r--tests/codegen/default-visibility.rs16
-rw-r--r--tests/run-make/msvc-lld-thinlto-imp-symbols/issue_81408.rs13
-rw-r--r--tests/run-make/msvc-lld-thinlto-imp-symbols/main.rs5
-rw-r--r--tests/run-make/msvc-lld-thinlto-imp-symbols/rmake.rs33
20 files changed, 225 insertions, 95 deletions
diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs
index 7be44dd51b5..33258cb46fa 100644
--- a/compiler/rustc_codegen_llvm/src/declare.rs
+++ b/compiler/rustc_codegen_llvm/src/declare.rs
@@ -84,10 +84,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
         unnamed: llvm::UnnamedAddr,
         fn_type: &'ll Type,
     ) -> &'ll Value {
-        // Declare C ABI functions with the visibility used by C by default.
-        let visibility = Visibility::from_generic(self.tcx.sess.default_visibility());
-
-        declare_raw_fn(self, name, llvm::CCallConv, unnamed, visibility, fn_type)
+        // Visibility should always be default for declarations, otherwise the linker may report an
+        // error.
+        declare_raw_fn(self, name, llvm::CCallConv, unnamed, Visibility::Default, fn_type)
     }
 
     /// Declare an entry Function
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index e22dd8373bd..e3d11cfaf4f 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -2164,8 +2164,14 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool {
             && tcx.sess.opts.cg.prefer_dynamic)
     );
 
+    // We need to generate _imp__ symbol if we are generating an rlib or we include one
+    // indirectly from ThinLTO. In theory these are not needed as ThinLTO could resolve
+    // these, but it currently does not do so.
+    let can_have_static_objects =
+        tcx.sess.lto() == Lto::Thin || tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib);
+
     tcx.sess.target.is_like_windows &&
-        tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib) &&
+    can_have_static_objects   &&
     // ThinLTO can't handle this workaround in all cases, so we don't
     // emit the `__imp_` symbols. Instead we make them unnecessary by disallowing
     // dynamic linking when linker plugin LTO is enabled.
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index 3b6197c6759..fdefec33eeb 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -444,7 +444,6 @@ where
     for &arg in &state.value.var_values.var_values.as_slice()
         [orig_values.len()..state.value.var_values.len()]
     {
-        // FIXME: This is so ugly.
         let unconstrained = delegate.fresh_var_for_kind_with_span(arg, span);
         orig_values.push(unconstrained);
     }
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 8391cdb2ca1..458edf920ab 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -92,7 +92,6 @@ where
 #[derive_where(Clone, Debug, Default; I: Interner)]
 #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
 #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
-// FIXME: This can be made crate-private once `EvalCtxt` also lives in this crate.
 struct NestedGoals<I: Interner> {
     /// These normalizes-to goals are treated specially during the evaluation
     /// loop. In each iteration we take the RHS of the projection, replace it with
@@ -421,6 +420,7 @@ where
         let (normalization_nested_goals, certainty) =
             self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);
         self.inspect.goal_evaluation(goal_evaluation);
+
         // FIXME: We previously had an assert here that checked that recomputing
         // a goal after applying its constraints did not change its response.
         //
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 97f7c71f3fc..8fe39bb4ee1 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -10,9 +10,6 @@
 //!
 //! For a high-level overview of how this solver works, check out the relevant
 //! section of the rustc-dev-guide.
-//!
-//! FIXME(@lcnr): Write that section. If you read this before then ask me
-//! about it on zulip.
 
 mod alias_relate;
 mod assembly;
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index fc9c634942b..005b293621a 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -899,7 +899,7 @@ where
                 for ty in types.iter() {
                     // We can't find the intersection if the types used are generic.
                     //
-                    // FIXME(effects) do we want to look at where clauses to get some
+                    // FIXME(effects): do we want to look at where clauses to get some
                     // clue for the case where generic types are being used?
                     let Some(kind) = ty::EffectKind::try_from_ty(cx, ty) else {
                         return Err(NoSolution);
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 2074bdec485..5828b2ecf34 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -108,7 +108,6 @@ where
         ecx: &mut EvalCtxt<'_, D>,
         _guar: I::ErrorGuaranteed,
     ) -> Result<Candidate<I>, NoSolution> {
-        // FIXME: don't need to enter a probe here.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -463,7 +462,6 @@ where
         // Async coroutine unconditionally implement `Future`
         // Technically, we need to check that the future output type is Sized,
         // but that's already proven by the coroutine being WF.
-        // FIXME: use `consider_implied`
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -489,7 +487,6 @@ where
         // Gen coroutines unconditionally implement `Iterator`
         // Technically, we need to check that the iterator output type is Sized,
         // but that's already proven by the coroutines being WF.
-        // FIXME: use `consider_implied`
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -512,8 +509,7 @@ where
             return Err(NoSolution);
         }
 
-        // Gen coroutines unconditionally implement `FusedIterator`
-        // FIXME: use `consider_implied`
+        // Gen coroutines unconditionally implement `FusedIterator`.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -539,7 +535,6 @@ where
         // Gen coroutines unconditionally implement `Iterator`
         // Technically, we need to check that the iterator output type is Sized,
         // but that's already proven by the coroutines being WF.
-        // FIXME: use `consider_implied`
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
@@ -610,7 +605,7 @@ where
             return Err(NoSolution);
         }
 
-        // FIXME(-Znext-solver): Implement this when we get const working in the new solver
+        // FIXME(effects): Implement this when we get const working in the new solver
 
         // `Destruct` is automatically implemented for every type in
         // non-const environments.
@@ -631,8 +626,6 @@ where
             return Err(NoSolution);
         }
 
-        // FIXME: This actually should destructure the `Result` we get from transmutability and
-        // register candidates. We probably need to register >1 since we may have an OR of ANDs.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let certainty = ecx.is_transmutable(
                 goal.param_env,
diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs
index b9767bf906b..71e533b8cc5 100644
--- a/compiler/rustc_target/src/abi/call/powerpc64.rs
+++ b/compiler/rustc_target/src/abi/call/powerpc64.rs
@@ -10,6 +10,7 @@ use crate::spec::HasTargetSpec;
 enum ABI {
     ELFv1, // original ABI used for powerpc64 (big-endian)
     ELFv2, // newer ABI used for powerpc64le and musl (both endians)
+    AIX,   // used by AIX OS, big-endian only
 }
 use ABI::*;
 
@@ -23,9 +24,9 @@ where
     C: HasDataLayout,
 {
     arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
-        // ELFv1 only passes one-member aggregates transparently.
+        // ELFv1 and AIX only passes one-member aggregates transparently.
         // ELFv2 passes up to eight uniquely addressable members.
-        if (abi == ELFv1 && arg.layout.size > unit.size)
+        if ((abi == ELFv1 || abi == AIX) && arg.layout.size > unit.size)
             || arg.layout.size > unit.size.checked_mul(8, cx).unwrap()
         {
             return None;
@@ -55,8 +56,15 @@ where
         return;
     }
 
+    // The AIX ABI expect byval for aggregates
+    // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp.
+    if !is_ret && abi == AIX {
+        arg.pass_by_stack_offset(None);
+        return;
+    }
+
     // The ELFv1 ABI doesn't return aggregates in registers
-    if is_ret && abi == ELFv1 {
+    if is_ret && (abi == ELFv1 || abi == AIX) {
         arg.make_indirect();
         return;
     }
@@ -93,6 +101,8 @@ where
 {
     let abi = if cx.target_spec().env == "musl" {
         ELFv2
+    } else if cx.target_spec().os == "aix" {
+        AIX
     } else {
         match cx.data_layout().endian {
             Endian::Big => ELFv1,
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 5c344930314..df9ac2b80fd 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -223,6 +223,8 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         if eligible { Ok(Some(node_item.item.def_id)) } else { Ok(None) }
     }
 
+    // FIXME: This actually should destructure the `Result` we get from transmutability and
+    // register candidates. We probably need to register >1 since we may have an OR of ANDs.
     fn is_transmutable(
         &self,
         param_env: ty::ParamEnv<'tcx>,
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 3fee73d6e5d..df9776ff5f8 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -230,6 +230,9 @@ h4.code-header {
 	padding: 0;
 	white-space: pre-wrap;
 }
+.structfield {
+	margin: 0.6em 0;
+}
 
 #crate-search,
 h1, h2, h3, h4, h5, h6,
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index bd0ed6321bc..63d05886166 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -9,11 +9,13 @@ use std::process::Command;
 use tracing::*;
 
 use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
+use crate::header::auxiliary::{AuxProps, parse_and_update_aux};
 use crate::header::cfg::{MatchOutcome, parse_cfg_name_directive};
 use crate::header::needs::CachedNeedsConditions;
 use crate::util::static_regex;
 use crate::{extract_cdb_version, extract_gdb_version};
 
+pub(crate) mod auxiliary;
 mod cfg;
 mod needs;
 #[cfg(test)]
@@ -33,9 +35,10 @@ impl HeadersCache {
 /// the test.
 #[derive(Default)]
 pub struct EarlyProps {
-    pub aux: Vec<String>,
-    pub aux_bin: Vec<String>,
-    pub aux_crate: Vec<(String, String)>,
+    /// Auxiliary crates that should be built and made available to this test.
+    /// Included in [`EarlyProps`] so that the indicated files can participate
+    /// in up-to-date checking. Building happens via [`TestProps::aux`] instead.
+    pub(crate) aux: AuxProps,
     pub revisions: Vec<String>,
 }
 
@@ -55,21 +58,7 @@ impl EarlyProps {
             testfile,
             rdr,
             &mut |HeaderLine { directive: ln, .. }| {
-                config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| {
-                    r.trim().to_string()
-                });
-                config.push_name_value_directive(
-                    ln,
-                    directives::AUX_BIN,
-                    &mut props.aux_bin,
-                    |r| r.trim().to_string(),
-                );
-                config.push_name_value_directive(
-                    ln,
-                    directives::AUX_CRATE,
-                    &mut props.aux_crate,
-                    Config::parse_aux_crate,
-                );
+                parse_and_update_aux(config, ln, &mut props.aux);
                 config.parse_and_update_revisions(ln, &mut props.revisions);
             },
         );
@@ -98,18 +87,8 @@ pub struct TestProps {
     // If present, the name of a file that this test should match when
     // pretty-printed
     pub pp_exact: Option<PathBuf>,
-    // Other crates that should be compiled (typically from the same
-    // directory as the test, but for backwards compatibility reasons
-    // we also check the auxiliary directory)
-    pub aux_builds: Vec<String>,
-    // Auxiliary crates that should be compiled as `#![crate_type = "bin"]`.
-    pub aux_bins: Vec<String>,
-    // Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies
-    // to build and pass with the `--extern` flag.
-    pub aux_crates: Vec<(String, String)>,
-    /// Similar to `aux_builds`, but also passes the resulting dylib path to
-    /// `-Zcodegen-backend`.
-    pub aux_codegen_backend: Option<String>,
+    /// Auxiliary crates that should be built and made available to this test.
+    pub(crate) aux: AuxProps,
     // Environment settings to use for compiling
     pub rustc_env: Vec<(String, String)>,
     // Environment variables to unset prior to compiling.
@@ -276,10 +255,7 @@ impl TestProps {
             run_flags: vec![],
             doc_flags: vec![],
             pp_exact: None,
-            aux_builds: vec![],
-            aux_bins: vec![],
-            aux_crates: vec![],
-            aux_codegen_backend: None,
+            aux: Default::default(),
             revisions: vec![],
             rustc_env: vec![
                 ("RUSTC_ICE".to_string(), "0".to_string()),
@@ -454,21 +430,10 @@ impl TestProps {
                         PRETTY_COMPARE_ONLY,
                         &mut self.pretty_compare_only,
                     );
-                    config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| {
-                        r.trim().to_string()
-                    });
-                    config.push_name_value_directive(ln, AUX_BIN, &mut self.aux_bins, |r| {
-                        r.trim().to_string()
-                    });
-                    config.push_name_value_directive(
-                        ln,
-                        AUX_CRATE,
-                        &mut self.aux_crates,
-                        Config::parse_aux_crate,
-                    );
-                    if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
-                        self.aux_codegen_backend = Some(r.trim().to_owned());
-                    }
+
+                    // Call a helper method to deal with aux-related directives.
+                    parse_and_update_aux(config, ln, &mut self.aux);
+
                     config.push_name_value_directive(
                         ln,
                         EXEC_ENV,
@@ -942,14 +907,6 @@ fn iter_header(
 }
 
 impl Config {
-    fn parse_aux_crate(r: String) -> (String, String) {
-        let mut parts = r.trim().splitn(2, '=');
-        (
-            parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(),
-            parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(),
-        )
-    }
-
     fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) {
         if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
             let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
diff --git a/src/tools/compiletest/src/header/auxiliary.rs b/src/tools/compiletest/src/header/auxiliary.rs
new file mode 100644
index 00000000000..6f6538ce196
--- /dev/null
+++ b/src/tools/compiletest/src/header/auxiliary.rs
@@ -0,0 +1,60 @@
+//! Code for dealing with test directives that request an "auxiliary" crate to
+//! be built and made available to the test in some way.
+
+use std::iter;
+
+use crate::common::Config;
+use crate::header::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE};
+
+/// Properties parsed from `aux-*` test directives.
+#[derive(Clone, Debug, Default)]
+pub(crate) struct AuxProps {
+    /// Other crates that should be built and made available to this test.
+    /// These are filenames relative to `./auxiliary/` in the test's directory.
+    pub(crate) builds: Vec<String>,
+    /// Auxiliary crates that should be compiled as `#![crate_type = "bin"]`.
+    pub(crate) bins: Vec<String>,
+    /// Similar to `builds`, but a list of NAME=somelib.rs of dependencies
+    /// to build and pass with the `--extern` flag.
+    pub(crate) crates: Vec<(String, String)>,
+    /// Similar to `builds`, but also uses the resulting dylib as a
+    /// `-Zcodegen-backend` when compiling the test file.
+    pub(crate) codegen_backend: Option<String>,
+}
+
+impl AuxProps {
+    /// Yields all of the paths (relative to `./auxiliary/`) that have been
+    /// specified in `aux-*` directives for this test.
+    pub(crate) fn all_aux_path_strings(&self) -> impl Iterator<Item = &str> {
+        let Self { builds, bins, crates, codegen_backend } = self;
+
+        iter::empty()
+            .chain(builds.iter().map(String::as_str))
+            .chain(bins.iter().map(String::as_str))
+            .chain(crates.iter().map(|(_, path)| path.as_str()))
+            .chain(codegen_backend.iter().map(String::as_str))
+    }
+}
+
+/// If the given test directive line contains an `aux-*` directive, parse it
+/// and update [`AuxProps`] accordingly.
+pub(super) fn parse_and_update_aux(config: &Config, ln: &str, aux: &mut AuxProps) {
+    if !ln.starts_with("aux-") {
+        return;
+    }
+
+    config.push_name_value_directive(ln, AUX_BUILD, &mut aux.builds, |r| r.trim().to_string());
+    config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string());
+    config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate);
+    if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
+        aux.codegen_backend = Some(r.trim().to_owned());
+    }
+}
+
+fn parse_aux_crate(r: String) -> (String, String) {
+    let mut parts = r.trim().splitn(2, '=');
+    (
+        parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(),
+        parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(),
+    )
+}
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 10ec2a1806f..22dfa349e2b 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -242,7 +242,8 @@ fn aux_build() {
         //@ aux-build: b.rs
         "
         )
-        .aux,
+        .aux
+        .builds,
         vec!["a.rs", "b.rs"],
     );
 }
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 98375a21b04..30d1644b148 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -862,7 +862,8 @@ fn files_related_to_test(
         related.push(testpaths.file.clone());
     }
 
-    for aux in &props.aux {
+    for aux in props.aux.all_aux_path_strings() {
+        // FIXME(Zalathar): Perform all `auxiliary` path resolution in one place.
         let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
         related.push(path);
     }
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 1baf0c56e37..46f7b9c0e7d 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -841,13 +841,13 @@ impl<'test> TestCx<'test> {
     /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
     fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes {
         if self.props.build_aux_docs {
-            for rel_ab in &self.props.aux_builds {
+            for rel_ab in &self.props.aux.builds {
                 let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab);
-                let aux_props =
+                let props_for_aux =
                     self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
                 let aux_cx = TestCx {
                     config: self.config,
-                    props: &aux_props,
+                    props: &props_for_aux,
                     testpaths: &aux_testpaths,
                     revision: self.revision,
                 };
@@ -1059,11 +1059,11 @@ impl<'test> TestCx<'test> {
     fn aux_output_dir(&self) -> PathBuf {
         let aux_dir = self.aux_output_dir_name();
 
-        if !self.props.aux_builds.is_empty() {
+        if !self.props.aux.builds.is_empty() {
             remove_and_create_dir_all(&aux_dir);
         }
 
-        if !self.props.aux_bins.is_empty() {
+        if !self.props.aux.bins.is_empty() {
             let aux_bin_dir = self.aux_bin_output_dir_name();
             remove_and_create_dir_all(&aux_dir);
             remove_and_create_dir_all(&aux_bin_dir);
@@ -1073,15 +1073,15 @@ impl<'test> TestCx<'test> {
     }
 
     fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Path, rustc: &mut Command) {
-        for rel_ab in &self.props.aux_builds {
+        for rel_ab in &self.props.aux.builds {
             self.build_auxiliary(of, rel_ab, &aux_dir, false /* is_bin */);
         }
 
-        for rel_ab in &self.props.aux_bins {
+        for rel_ab in &self.props.aux.bins {
             self.build_auxiliary(of, rel_ab, &aux_dir, true /* is_bin */);
         }
 
-        for (aux_name, aux_path) in &self.props.aux_crates {
+        for (aux_name, aux_path) in &self.props.aux.crates {
             let aux_type = self.build_auxiliary(of, &aux_path, &aux_dir, false /* is_bin */);
             let lib_name =
                 get_lib_name(&aux_path.trim_end_matches(".rs").replace('-', "_"), aux_type);
@@ -1097,7 +1097,7 @@ impl<'test> TestCx<'test> {
 
         // Build any `//@ aux-codegen-backend`, and pass the resulting library
         // to `-Zcodegen-backend` when compiling the test file.
-        if let Some(aux_file) = &self.props.aux_codegen_backend {
+        if let Some(aux_file) = &self.props.aux.codegen_backend {
             let aux_type = self.build_auxiliary(of, aux_file, aux_dir, false);
             if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) {
                 let lib_path = aux_dir.join(&lib_name);
diff --git a/tests/assembly/powerpc64-struct-abi.rs b/tests/assembly/powerpc64-struct-abi.rs
index 9a3540d8b41..7052937acf6 100644
--- a/tests/assembly/powerpc64-struct-abi.rs
+++ b/tests/assembly/powerpc64-struct-abi.rs
@@ -1,4 +1,4 @@
-//@ revisions: elfv1-be elfv2-be elfv2-le
+//@ revisions: elfv1-be elfv2-be elfv2-le aix
 //@ assembly-output: emit-asm
 //@ compile-flags: -O
 //@[elfv1-be] compile-flags: --target powerpc64-unknown-linux-gnu
@@ -7,8 +7,13 @@
 //@[elfv2-be] needs-llvm-components: powerpc
 //@[elfv2-le] compile-flags: --target powerpc64le-unknown-linux-gnu
 //@[elfv2-le] needs-llvm-components: powerpc
+//@[aix] compile-flags: --target powerpc64-ibm-aix
+//@[aix] needs-llvm-components: powerpc
 //@[elfv1-be] filecheck-flags: --check-prefix be
 //@[elfv2-be] filecheck-flags: --check-prefix be
+//@[elfv1-be] filecheck-flags: --check-prefix elf
+//@[elfv2-be] filecheck-flags: --check-prefix elf
+//@[elfv2-le] filecheck-flags: --check-prefix elf
 
 #![feature(no_core, lang_items)]
 #![no_std]
@@ -44,6 +49,10 @@ struct FiveU16s(u16, u16, u16, u16, u16);
 struct ThreeU8s(u8, u8, u8);
 
 // CHECK-LABEL: read_large
+// aix: lwz [[REG1:.*]], 16(4)
+// aix-NEXT: lxvd2x 0, 0, 4
+// aix-NEXT: stw [[REG1]], 16(3)
+// aix-NEXT: stxvd2x 0, 0, 3
 // be: lwz [[REG1:.*]], 16(4)
 // be-NEXT: stw [[REG1]], 16(3)
 // be-NEXT: ld [[REG2:.*]], 8(4)
@@ -61,6 +70,10 @@ extern "C" fn read_large(x: &FiveU32s) -> FiveU32s {
 }
 
 // CHECK-LABEL: read_medium
+// aix: lhz [[REG1:.*]], 8(4)
+// aix-NEXT: ld [[REG2:.*]], 0(4)
+// aix-NEXT: sth [[REG1]], 8(3)
+// aix-NEXT: std [[REG2]], 0(3)
 // elfv1-be: lhz [[REG1:.*]], 8(4)
 // elfv1-be-NEXT: ld [[REG2:.*]], 0(4)
 // elfv1-be-NEXT: sth [[REG1]], 8(3)
@@ -78,6 +91,10 @@ extern "C" fn read_medium(x: &FiveU16s) -> FiveU16s {
 }
 
 // CHECK-LABEL: read_small
+// aix: lbz [[REG1:.*]], 2(4)
+// aix-NEXT: lhz [[REG2:.*]], 0(4)
+// aix-NEXT: stb [[REG1]], 2(3)
+// aix-NEXT: sth [[REG2]], 0(3)
 // elfv1-be: lbz [[REG1:.*]], 2(4)
 // elfv1-be-NEXT: lhz [[REG2:.*]], 0(4)
 // elfv1-be-NEXT: stb [[REG1]], 2(3)
@@ -95,9 +112,17 @@ extern "C" fn read_small(x: &ThreeU8s) -> ThreeU8s {
 }
 
 // CHECK-LABEL: write_large
-// CHECK: std 3, 0(6)
+// aix: std 3, 48(1)
+// aix-NEXT: rldicl [[REG1:.*]], 5, 32, 32
+// aix-NEXT: std 5, 64(1)
+// aix-NEXT: std 4, 56(1)
+// aix-NEXT: stw [[REG1]], 16(6)
+// aix-NEXT: addi [[REG2:.*]], 1, 48
+// aix-NEXT: lxvd2x 0, 0, [[REG2]]
+// aix-NEXT: stxvd2x 0, 0, 6
+// elf: std 3, 0(6)
 // be-NEXT: rldicl [[REG1:.*]], 5, 32, 32
-// CHECK-NEXT: std 4, 8(6)
+// elf-NEXT: std 4, 8(6)
 // be-NEXT: stw [[REG1]], 16(6)
 // elfv2-le-NEXT: stw 5, 16(6)
 // CHECK-NEXT: blr
@@ -107,7 +132,12 @@ extern "C" fn write_large(x: FiveU32s, dest: &mut FiveU32s) {
 }
 
 // CHECK-LABEL: write_medium
-// CHECK: std 3, 0(5)
+// aix: std 4, 56(1)
+// aix-NEXT: rldicl [[REG1:.*]], 4, 16, 48
+// aix-NEXT: std 3, 48(1)
+// aix-NEXT: std 3, 0(5)
+// aix-NEXT: sth [[REG1]], 8(5)
+// elf: std 3, 0(5)
 // be-NEXT: rldicl [[REG1:.*]], 4, 16, 48
 // be-NEXT: sth [[REG1]], 8(5)
 // elfv2-le-NEXT: sth 4, 8(5)
@@ -118,6 +148,11 @@ extern "C" fn write_medium(x: FiveU16s, dest: &mut FiveU16s) {
 }
 
 // CHECK-LABEL: write_small
+// aix: std 3, 48(1)
+// aix-NEXT: rldicl [[REG1:.*]], 3, 16, 48
+// aix-NEXT: sth 3, 0(4)
+// aix-NEXT: lbz 3, 50(1)
+// aix-NEXT: stb [[REG1]], 2(4)
 // be: stb 3, 2(4)
 // be-NEXT: srwi [[REG1:.*]], 3, 8
 // be-NEXT: sth [[REG1]], 0(4)
diff --git a/tests/codegen/default-visibility.rs b/tests/codegen/default-visibility.rs
index 884d386ec20..88ff9fee254 100644
--- a/tests/codegen/default-visibility.rs
+++ b/tests/codegen/default-visibility.rs
@@ -31,3 +31,19 @@ pub static tested_symbol: [u8; 6] = *b"foobar";
 // PROTECTED:    @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = protected constant
 // INTERPOSABLE: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant
 // DEFAULT:      @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant
+
+pub fn do_memcmp(left: &[u8], right: &[u8]) -> i32 {
+    left.cmp(right) as i32
+}
+
+// CHECK: define {{.*}} @{{.*}}do_memcmp{{.*}} {
+// CHECK: }
+
+// `do_memcmp` should invoke core::intrinsic::compare_bytes which emits a call
+// to the C symbol `memcmp` (at least on x86_64-unknown-linux-gnu). This symbol
+// should *not* be declared hidden or protected.
+
+// HIDDEN:       declare i32 @memcmp
+// PROTECTED:    declare i32 @memcmp
+// INTERPOSABLE: declare i32 @memcmp
+// DEFAULT:      declare i32 @memcmp
diff --git a/tests/run-make/msvc-lld-thinlto-imp-symbols/issue_81408.rs b/tests/run-make/msvc-lld-thinlto-imp-symbols/issue_81408.rs
new file mode 100644
index 00000000000..afb0dc42f44
--- /dev/null
+++ b/tests/run-make/msvc-lld-thinlto-imp-symbols/issue_81408.rs
@@ -0,0 +1,13 @@
+use std::sync::atomic::{AtomicPtr, Ordering};
+
+#[inline(always)]
+pub fn memrchr() {
+    fn detect() {}
+
+    static CROSS_CRATE_STATIC_ITEM: AtomicPtr<()> = AtomicPtr::new(detect as *mut ());
+
+    unsafe {
+        let fun = CROSS_CRATE_STATIC_ITEM.load(Ordering::SeqCst);
+        std::mem::transmute::<*mut (), fn()>(fun)()
+    }
+}
diff --git a/tests/run-make/msvc-lld-thinlto-imp-symbols/main.rs b/tests/run-make/msvc-lld-thinlto-imp-symbols/main.rs
new file mode 100644
index 00000000000..2d2d2e68125
--- /dev/null
+++ b/tests/run-make/msvc-lld-thinlto-imp-symbols/main.rs
@@ -0,0 +1,5 @@
+extern crate issue_81408;
+
+fn main() {
+    issue_81408::memrchr();
+}
diff --git a/tests/run-make/msvc-lld-thinlto-imp-symbols/rmake.rs b/tests/run-make/msvc-lld-thinlto-imp-symbols/rmake.rs
new file mode 100644
index 00000000000..3db1ae3452d
--- /dev/null
+++ b/tests/run-make/msvc-lld-thinlto-imp-symbols/rmake.rs
@@ -0,0 +1,33 @@
+// This is a non-regression test for issue #81408 involving an lld bug and ThinLTO, on windows.
+// MSVC's link.exe doesn't need any workarounds in rustc, but lld does, so we'll check that the
+// binary runs successfully instead of using a codegen test.
+
+//@ only-x86_64-pc-windows-msvc
+//@ needs-rust-lld
+//@ ignore-cross-compile: the built binary is executed
+
+use run_make_support::{run, rustc};
+
+fn test_with_linker(linker: &str) {
+    rustc().input("issue_81408.rs").crate_name("issue_81408").crate_type("lib").opt().run();
+    rustc()
+        .input("main.rs")
+        .crate_type("bin")
+        .arg("-Clto=thin")
+        .opt()
+        .arg(&format!("-Clinker={linker}"))
+        .extern_("issue_81408", "libissue_81408.rlib")
+        .run();
+
+    // To make possible failures clearer, print an intro that will only be shown if the test does
+    // fail when running the binary.
+    eprint!("Running binary linked with {linker}... ");
+    run("main");
+    eprintln!("ok");
+}
+
+fn main() {
+    // We want the reproducer to work when linked with both linkers.
+    test_with_linker("link");
+    test_with_linker("rust-lld");
+}