From a64b2a95ff3155fd6dc85312cb4ceac232d28d02 Mon Sep 17 00:00:00 2001
From: Thom Chiovoloni <chiovolonit@gmail.com>
Date: Wed, 11 May 2022 01:55:00 -0700
Subject: [PATCH] Move `#[used]` check for Mach-O to `rustc_typeck` from
 `rustc_codegen_llvm`

---
 compiler/rustc_codegen_llvm/src/consts.rs | 34 +++++------------------
 compiler/rustc_typeck/src/collect.rs      | 32 ++++++++++++++++++++-
 2 files changed, 38 insertions(+), 28 deletions(-)

diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index a1ab590335a..ea5df912438 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -543,33 +543,13 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
                 // in the handling of `.init_array` (the static constructor list) in versions of
                 // the gold linker (prior to the one released with binutils 2.36).
                 //
-                // However, unconditional use of `llvm.compiler.used` caused a nontrivial amount of
-                // ecosystem breakage, especially on Mach-O targets. To resolve this, we compile it
-                // as llvm.compiler.used on ELF targets and llvm.used elsewhere, which should be
-                // equivalent to how we compiled `#[used]` before LLVM 13, as `llvm.used` and
-                // `llvm.compiler.used` were treated the same on ELF targets prior in earlier LLVM
-                // versions (additionally, it seems to be how Clang handles `__attribute__((used))`,
-                // perhaps for similar compatibility-motivated reasons).
-                //
-                // See https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146 and
-                // following comments for some discussion of this.
-                //
-                // The final wrinkle is it's not really clear how to tell if we're going to output
-                // ELF, so it's been approximated as "not like wasm, osx, or windows", which is
-                // not exactly correct, but is pretty close and hopefully handles all the platforms
-                // platforms where old versions of `ld.gold` are likely to show up.
-                //
-                // All this is subject to change in the future. Which is a good thing, because this
-                // probably should be firmed up somehow!
-                let seems_like_elf = !(self.tcx.sess.target.is_like_osx
-                    || self.tcx.sess.target.is_like_windows
-                    || self.tcx.sess.target.is_like_wasm);
-
-                if seems_like_elf {
-                    self.add_compiler_used_global(g);
-                } else {
-                    self.add_used_global(g);
-                }
+                // That said, we only ever emit these when compiling for ELF targets, unless
+                // `#[used(compiler)]` is explicitly requested. This is to avoid similar breakage
+                // on other targets, in particular MachO targets have *their* static constructor
+                // lists broken if `llvm.compiler.used` is emitted rather than llvm.used. However,
+                // that check happens when assigning the `CodegenFnAttrFlags` in `rustc_typeck`,
+                // so we don't need to take care of it here.
+                self.add_compiler_used_global(g);
             }
             if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
                 // `USED` and `USED_LINKER` can't be used together.
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 2e0e026631b..e13c8322e33 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -2850,7 +2850,37 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
                         )
                         .emit();
                 }
-                None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED,
+                None => {
+                    // Unfortunately, unconditionally using `llvm.used` causes
+                    // issues in handling `.init_array` with the gold linker,
+                    // but using `llvm.compiler.used` caused a nontrival amount
+                    // of unintentional ecosystem breakage -- particularly on
+                    // Mach-O targets.
+                    //
+                    // As a result, we emit `llvm.compiler.used` only on ELF
+                    // targets. This is somewhat ad-hoc, but actually follows
+                    // our pre-LLVM 13 behavior (prior to the ecosystem
+                    // breakage), and seems to match `clang`'s behavior as well
+                    // (both before and after LLVM 13), possibly because they
+                    // have similar compatibility concerns to us. See
+                    // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
+                    // and following comments for some discussion of this, as
+                    // well as the comments in `rustc_codegen_llvm` where these
+                    // flags are handled.
+                    //
+                    // Anyway, to be clear: this is still up in the air
+                    // somewhat, and is subject to change in the future (which
+                    // is a good thing, because this would ideally be a bit
+                    // more firmed up).
+                    let is_like_elf = !(tcx.sess.target.is_like_osx
+                        || tcx.sess.target.is_like_windows
+                        || tcx.sess.target.is_like_wasm);
+                    codegen_fn_attrs.flags = if is_like_elf {
+                        CodegenFnAttrFlags::USED
+                    } else {
+                        CodegenFnAttrFlags::USED_LINKER
+                    };
+                }
             }
         } else if attr.has_name(sym::cmse_nonsecure_entry) {
             if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) {