about summary refs log tree commit diff
path: root/compiler/rustc_codegen_llvm/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_llvm/src')
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/base.rs24
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs9
-rw-r--r--compiler/rustc_codegen_llvm/src/common.rs14
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs226
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs110
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs101
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs27
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs144
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/mod.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs57
13 files changed, 499 insertions, 237 deletions
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index ac7583f5666..11be7041167 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -215,9 +215,9 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 let align = attrs.pointee_align.unwrap_or(self.layout.align.abi);
                 OperandValue::Ref(PlaceValue::new_sized(val, align)).store(bx, dst);
             }
-            // Unsized indirect qrguments
+            // Unsized indirect arguments cannot be stored
             PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
-                bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
+                bug!("unsized `ArgAbi` cannot be stored");
             }
             PassMode::Cast { cast, pad_i32: _ } => {
                 // The ABI mandates that the value is passed as a different struct representation.
@@ -272,12 +272,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 OperandValue::Pair(next(), next()).store(bx, dst);
             }
             PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
-                let place_val = PlaceValue {
-                    llval: next(),
-                    llextra: Some(next()),
-                    align: self.layout.align.abi,
-                };
-                OperandValue::Ref(place_val).store(bx, dst);
+                bug!("unsized `ArgAbi` cannot be stored");
             }
             PassMode::Direct(_)
             | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ }
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index 38c1d3b53e8..b79176e9098 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -240,6 +240,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                 }
                 InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
                     constraints.extend_from_slice(&[
+                        "~{fflags}".to_string(),
                         "~{vtype}".to_string(),
                         "~{vl}".to_string(),
                         "~{vxsat}".to_string(),
diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs
index 9cc5d8dbc21..978134cc32b 100644
--- a/compiler/rustc_codegen_llvm/src/base.rs
+++ b/compiler/rustc_codegen_llvm/src/base.rs
@@ -109,18 +109,36 @@ pub(crate) fn compile_codegen_unit(
                 attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs);
             }
 
+            // Define Objective-C module info and module flags. Note, the module info will
+            // also be added to the `llvm.compiler.used` variable, created later.
+            //
+            // These are only necessary when we need the linker to do its Objective-C-specific
+            // magic. We could theoretically do it unconditionally, but at a slight cost to linker
+            // performance in the common case where it's unnecessary.
+            if !cx.objc_classrefs.borrow().is_empty() || !cx.objc_selrefs.borrow().is_empty() {
+                if cx.objc_abi_version() == 1 {
+                    cx.define_objc_module_info();
+                }
+                cx.add_objc_module_flags();
+            }
+
             // Finalize code coverage by injecting the coverage map. Note, the coverage map will
             // also be added to the `llvm.compiler.used` variable, created next.
             if cx.sess().instrument_coverage() {
                 cx.coverageinfo_finalize();
             }
 
-            // Create the llvm.used and llvm.compiler.used variables.
+            // Create the llvm.used variable.
             if !cx.used_statics.is_empty() {
                 cx.create_used_variable_impl(c"llvm.used", &cx.used_statics);
             }
-            if !cx.compiler_used_statics.is_empty() {
-                cx.create_used_variable_impl(c"llvm.compiler.used", &cx.compiler_used_statics);
+
+            // Create the llvm.compiler.used variable.
+            {
+                let compiler_used_statics = cx.compiler_used_statics.borrow();
+                if !compiler_used_statics.is_empty() {
+                    cx.create_used_variable_impl(c"llvm.compiler.used", &compiler_used_statics);
+                }
             }
 
             // Run replace-all-uses-with for statics that need it. This must
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 7d0691366e6..0f17cc9063a 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -1091,16 +1091,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         ty: Ty<'tcx>,
         lhs: Self::Value,
         rhs: Self::Value,
-    ) -> Option<Self::Value> {
-        // FIXME: See comment on the definition of `three_way_compare`.
-        if crate::llvm_util::get_version() < (20, 0, 0) {
-            return None;
-        }
-
+    ) -> Self::Value {
         let size = ty.primitive_size(self.tcx);
         let name = if ty.is_signed() { "llvm.scmp" } else { "llvm.ucmp" };
 
-        Some(self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs]))
+        self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs])
     }
 
     /* Miscellaneous instructions */
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 11b79a7fe68..aa2df46329f 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -108,6 +108,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
         bytes_in_context(self.llcx(), bytes)
     }
 
+    pub(crate) fn null_terminate_const_bytes(&self, bytes: &[u8]) -> &'ll Value {
+        null_terminate_bytes_in_context(self.llcx(), bytes)
+    }
+
     pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value {
         unsafe {
             let idx = c_uint::try_from(idx).expect("LLVMGetAggregateElement index overflow");
@@ -381,6 +385,16 @@ pub(crate) fn bytes_in_context<'ll>(llcx: &'ll llvm::Context, bytes: &[u8]) -> &
     }
 }
 
+pub(crate) fn null_terminate_bytes_in_context<'ll>(
+    llcx: &'ll llvm::Context,
+    bytes: &[u8],
+) -> &'ll Value {
+    unsafe {
+        let ptr = bytes.as_ptr() as *const c_char;
+        llvm::LLVMConstStringInContext2(llcx, ptr, bytes.len(), FALSE)
+    }
+}
+
 pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value {
     let len = c_uint::try_from(elts.len()).expect("LLVMConstStructInContext elements len overflow");
     unsafe { llvm::LLVMConstNamedStruct(ty, elts.as_ptr(), len) }
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index dc9bb743560..a110ecbb75d 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -16,6 +16,7 @@ use rustc_middle::mir::mono::MonoItem;
 use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
 use rustc_middle::ty::{self, Instance};
 use rustc_middle::{bug, span_bug};
+use rustc_span::Symbol;
 use tracing::{debug, instrument, trace};
 
 use crate::common::CodegenCx;
@@ -331,6 +332,10 @@ impl<'ll> CodegenCx<'ll, '_> {
             }
 
             g
+        } else if let Some(classname) = fn_attrs.objc_class {
+            self.get_objc_classref(classname)
+        } else if let Some(methname) = fn_attrs.objc_selector {
+            self.get_objc_selref(methname)
         } else {
             check_and_apply_linkage(self, fn_attrs, llty, sym, def_id)
         };
@@ -543,8 +548,225 @@ impl<'ll> CodegenCx<'ll, '_> {
 
     /// Add a global value to a list to be stored in the `llvm.compiler.used` variable,
     /// an array of ptr.
-    pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) {
-        self.compiler_used_statics.push(global);
+    pub(crate) fn add_compiler_used_global(&self, global: &'ll Value) {
+        self.compiler_used_statics.borrow_mut().push(global);
+    }
+
+    // We do our best here to match what Clang does when compiling Objective-C natively.
+    // See Clang's `CGObjCCommonMac::CreateCStringLiteral`:
+    // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134
+    fn define_objc_classname(&self, classname: &str) -> &'ll Value {
+        assert_eq!(self.objc_abi_version(), 1);
+
+        let llval = self.null_terminate_const_bytes(classname.as_bytes());
+        let llty = self.val_ty(llval);
+        let sym = self.generate_local_symbol_name("OBJC_CLASS_NAME_");
+        let g = self.define_global(&sym, llty).unwrap_or_else(|| {
+            bug!("symbol `{}` is already defined", sym);
+        });
+        set_global_alignment(self, g, self.tcx.data_layout.i8_align.abi);
+        llvm::set_initializer(g, llval);
+        llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
+        llvm::set_section(g, c"__TEXT,__cstring,cstring_literals");
+        llvm::LLVMSetGlobalConstant(g, llvm::TRUE);
+        llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
+        self.add_compiler_used_global(g);
+
+        g
+    }
+
+    // We do our best here to match what Clang does when compiling Objective-C natively.
+    // See Clang's `ObjCNonFragileABITypesHelper`:
+    // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L6052
+    fn get_objc_class_t(&self) -> &'ll Type {
+        if let Some(class_t) = self.objc_class_t.get() {
+            return class_t;
+        }
+
+        assert_eq!(self.objc_abi_version(), 2);
+
+        // struct _class_t {
+        //     struct _class_t* isa;
+        //     struct _class_t* const superclass;
+        //     void* cache;
+        //     IMP* vtable;
+        //     struct class_ro_t* ro;
+        // }
+
+        let class_t = self.type_named_struct("struct._class_t");
+        let els = [self.type_ptr(); 5];
+        let packed = false;
+        self.set_struct_body(class_t, &els, packed);
+
+        self.objc_class_t.set(Some(class_t));
+        class_t
+    }
+
+    // We do our best here to match what Clang does when compiling Objective-C natively. We
+    // deduplicate references within a CGU, but we need a reference definition in each referencing
+    // CGU. All attempts at using external references to a single reference definition result in
+    // linker errors.
+    fn get_objc_classref(&self, classname: Symbol) -> &'ll Value {
+        let mut classrefs = self.objc_classrefs.borrow_mut();
+        if let Some(classref) = classrefs.get(&classname).copied() {
+            return classref;
+        }
+
+        let g = match self.objc_abi_version() {
+            1 => {
+                // See Clang's `CGObjCMac::EmitClassRefFromId`:
+                // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5205
+                let llval = self.define_objc_classname(classname.as_str());
+                let llty = self.type_ptr();
+                let sym = self.generate_local_symbol_name("OBJC_CLASS_REFERENCES_");
+                let g = self.define_global(&sym, llty).unwrap_or_else(|| {
+                    bug!("symbol `{}` is already defined", sym);
+                });
+                set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
+                llvm::set_initializer(g, llval);
+                llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
+                llvm::set_section(g, c"__OBJC,__cls_refs,literal_pointers,no_dead_strip");
+                self.add_compiler_used_global(g);
+                g
+            }
+            2 => {
+                // See Clang's `CGObjCNonFragileABIMac::EmitClassRefFromId`:
+                // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7423
+                let llval = {
+                    let extern_sym = format!("OBJC_CLASS_$_{}", classname.as_str());
+                    let extern_llty = self.get_objc_class_t();
+                    self.declare_global(&extern_sym, extern_llty)
+                };
+                let llty = self.type_ptr();
+                let sym = self.generate_local_symbol_name("OBJC_CLASSLIST_REFERENCES_$_");
+                let g = self.define_global(&sym, llty).unwrap_or_else(|| {
+                    bug!("symbol `{}` is already defined", sym);
+                });
+                set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
+                llvm::set_initializer(g, llval);
+                llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
+                llvm::set_section(g, c"__DATA,__objc_classrefs,regular,no_dead_strip");
+                self.add_compiler_used_global(g);
+                g
+            }
+            _ => unreachable!(),
+        };
+
+        classrefs.insert(classname, g);
+        g
+    }
+
+    // We do our best here to match what Clang does when compiling Objective-C natively. We
+    // deduplicate references within a CGU, but we need a reference definition in each referencing
+    // CGU. All attempts at using external references to a single reference definition result in
+    // linker errors.
+    //
+    // Newer versions of Apple Clang generate calls to `@"objc_msgSend$methname"` selector stub
+    // functions. We don't currently do that. The code we generate is closer to what Apple Clang
+    // generates with the `-fno-objc-msgsend-selector-stubs` option.
+    fn get_objc_selref(&self, methname: Symbol) -> &'ll Value {
+        let mut selrefs = self.objc_selrefs.borrow_mut();
+        if let Some(selref) = selrefs.get(&methname).copied() {
+            return selref;
+        }
+
+        let abi_version = self.objc_abi_version();
+
+        // See Clang's `CGObjCCommonMac::CreateCStringLiteral`:
+        // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134
+        let methname_llval = self.null_terminate_const_bytes(methname.as_str().as_bytes());
+        let methname_llty = self.val_ty(methname_llval);
+        let methname_sym = self.generate_local_symbol_name("OBJC_METH_VAR_NAME_");
+        let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| {
+            bug!("symbol `{}` is already defined", methname_sym);
+        });
+        set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align.abi);
+        llvm::set_initializer(methname_g, methname_llval);
+        llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage);
+        llvm::set_section(
+            methname_g,
+            match abi_version {
+                1 => c"__TEXT,__cstring,cstring_literals",
+                2 => c"__TEXT,__objc_methname,cstring_literals",
+                _ => unreachable!(),
+            },
+        );
+        llvm::LLVMSetGlobalConstant(methname_g, llvm::TRUE);
+        llvm::LLVMSetUnnamedAddress(methname_g, llvm::UnnamedAddr::Global);
+        self.add_compiler_used_global(methname_g);
+
+        // See Clang's `CGObjCMac::EmitSelectorAddr`:
+        // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5243
+        // And Clang's `CGObjCNonFragileABIMac::EmitSelectorAddr`:
+        // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7586
+        let selref_llval = methname_g;
+        let selref_llty = self.type_ptr();
+        let selref_sym = self.generate_local_symbol_name("OBJC_SELECTOR_REFERENCES_");
+        let selref_g = self.define_global(&selref_sym, selref_llty).unwrap_or_else(|| {
+            bug!("symbol `{}` is already defined", selref_sym);
+        });
+        set_global_alignment(self, selref_g, self.tcx.data_layout.pointer_align().abi);
+        llvm::set_initializer(selref_g, selref_llval);
+        llvm::set_externally_initialized(selref_g, true);
+        llvm::set_linkage(
+            selref_g,
+            match abi_version {
+                1 => llvm::Linkage::PrivateLinkage,
+                2 => llvm::Linkage::InternalLinkage,
+                _ => unreachable!(),
+            },
+        );
+        llvm::set_section(
+            selref_g,
+            match abi_version {
+                1 => c"__OBJC,__message_refs,literal_pointers,no_dead_strip",
+                2 => c"__DATA,__objc_selrefs,literal_pointers,no_dead_strip",
+                _ => unreachable!(),
+            },
+        );
+        self.add_compiler_used_global(selref_g);
+
+        selrefs.insert(methname, selref_g);
+        selref_g
+    }
+
+    // We do our best here to match what Clang does when compiling Objective-C natively.
+    // See Clang's `ObjCTypesHelper`:
+    // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5936
+    // And Clang's `CGObjCMac::EmitModuleInfo`:
+    // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5151
+    pub(crate) fn define_objc_module_info(&mut self) {
+        assert_eq!(self.objc_abi_version(), 1);
+
+        // struct _objc_module {
+        //     long version;                // Hardcoded to 7 in Clang.
+        //     long size;                   // sizeof(struct _objc_module)
+        //     char* name;                  // Hardcoded to classname "" in Clang.
+        //     struct _objc_symtab* symtab; // Null without class or category definitions.
+        //  }
+
+        let llty = self.type_named_struct("struct._objc_module");
+        let i32_llty = self.type_i32();
+        let ptr_llty = self.type_ptr();
+        let packed = false;
+        self.set_struct_body(llty, &[i32_llty, i32_llty, ptr_llty, ptr_llty], packed);
+
+        let version = self.const_uint(i32_llty, 7);
+        let size = self.const_uint(i32_llty, 16);
+        let name = self.define_objc_classname("");
+        let symtab = self.const_null(ptr_llty);
+        let llval = crate::common::named_struct(llty, &[version, size, name, symtab]);
+
+        let sym = "OBJC_MODULES";
+        let g = self.define_global(&sym, llty).unwrap_or_else(|| {
+            bug!("symbol `{}` is already defined", sym);
+        });
+        set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
+        llvm::set_initializer(g, llval);
+        llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
+        llvm::set_section(g, c"__OBJC,__module_info,regular,no_dead_strip");
+
+        self.add_compiler_used_global(g);
     }
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index a69fa54a54a..4a8ea11a3a8 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -26,7 +26,7 @@ use rustc_session::config::{
     BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet,
 };
 use rustc_span::source_map::Spanned;
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, Symbol};
 use rustc_symbol_mangling::mangle_internal_symbol;
 use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel};
 use smallvec::SmallVec;
@@ -119,7 +119,7 @@ pub(crate) struct FullCx<'ll, 'tcx> {
 
     /// Statics that will be placed in the llvm.compiler.used variable
     /// See <https://llvm.org/docs/LangRef.html#the-llvm-compiler-used-global-variable> for details
-    pub compiler_used_statics: Vec<&'ll Value>,
+    pub compiler_used_statics: RefCell<Vec<&'ll Value>>,
 
     /// Mapping of non-scalar types to llvm types.
     pub type_lowering: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), &'ll Type>>,
@@ -146,6 +146,15 @@ pub(crate) struct FullCx<'ll, 'tcx> {
     /// `global_asm!` needs to be able to find this new global so that it can
     /// compute the correct mangled symbol name to insert into the asm.
     pub renamed_statics: RefCell<FxHashMap<DefId, &'ll Value>>,
+
+    /// Cached Objective-C class type
+    pub objc_class_t: Cell<Option<&'ll Type>>,
+
+    /// Cache of Objective-C class references
+    pub objc_classrefs: RefCell<FxHashMap<Symbol, &'ll Value>>,
+
+    /// Cache of Objective-C selector references
+    pub objc_selrefs: RefCell<FxHashMap<Symbol, &'ll Value>>,
 }
 
 fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode {
@@ -172,35 +181,6 @@ pub(crate) unsafe fn create_module<'ll>(
     let mut target_data_layout = sess.target.data_layout.to_string();
     let llvm_version = llvm_util::get_version();
 
-    if llvm_version < (20, 0, 0) {
-        if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") {
-            // LLVM 20 defines three additional address spaces for alternate
-            // pointer kinds used in Windows.
-            // See https://github.com/llvm/llvm-project/pull/111879
-            target_data_layout =
-                target_data_layout.replace("-p270:32:32-p271:32:32-p272:64:64", "");
-        }
-        if sess.target.arch.starts_with("sparc") {
-            // LLVM 20 updates the sparc layout to correctly align 128 bit integers to 128 bit.
-            // See https://github.com/llvm/llvm-project/pull/106951
-            target_data_layout = target_data_layout.replace("-i128:128", "");
-        }
-        if sess.target.arch.starts_with("mips64") {
-            // LLVM 20 updates the mips64 layout to correctly align 128 bit integers to 128 bit.
-            // See https://github.com/llvm/llvm-project/pull/112084
-            target_data_layout = target_data_layout.replace("-i128:128", "");
-        }
-        if sess.target.arch.starts_with("powerpc64") {
-            // LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit.
-            // See https://github.com/llvm/llvm-project/pull/118004
-            target_data_layout = target_data_layout.replace("-i128:128", "");
-        }
-        if sess.target.arch.starts_with("wasm32") || sess.target.arch.starts_with("wasm64") {
-            // LLVM 20 updates the wasm(32|64) layout to correctly align 128 bit integers to 128 bit.
-            // See https://github.com/llvm/llvm-project/pull/119204
-            target_data_layout = target_data_layout.replace("-i128:128", "");
-        }
-    }
     if llvm_version < (21, 0, 0) {
         if sess.target.arch == "nvptx64" {
             // LLVM 21 updated the default layout on nvptx: https://github.com/llvm/llvm-project/pull/124961
@@ -644,7 +624,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
                 const_globals: Default::default(),
                 statics_to_rauw: RefCell::new(Vec::new()),
                 used_statics: Vec::new(),
-                compiler_used_statics: Vec::new(),
+                compiler_used_statics: Default::default(),
                 type_lowering: Default::default(),
                 scalar_lltypes: Default::default(),
                 coverage_cx,
@@ -655,6 +635,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
                 intrinsics: Default::default(),
                 local_gen_sym_counter: Cell::new(0),
                 renamed_statics: Default::default(),
+                objc_class_t: Cell::new(None),
+                objc_classrefs: Default::default(),
+                objc_selrefs: Default::default(),
             },
             PhantomData,
         )
@@ -679,6 +662,69 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
         llvm::set_linkage(g, llvm::Linkage::AppendingLinkage);
         llvm::set_section(g, c"llvm.metadata");
     }
+
+    /// The Objective-C ABI that is used.
+    ///
+    /// This corresponds to the `-fobjc-abi-version=` flag in Clang / GCC.
+    pub(crate) fn objc_abi_version(&self) -> u32 {
+        assert!(self.tcx.sess.target.is_like_darwin);
+        if self.tcx.sess.target.arch == "x86" && self.tcx.sess.target.os == "macos" {
+            // 32-bit x86 macOS uses ABI version 1 (a.k.a. the "fragile ABI").
+            1
+        } else {
+            // All other Darwin-like targets we support use ABI version 2
+            // (a.k.a the "non-fragile ABI").
+            2
+        }
+    }
+
+    // We do our best here to match what Clang does when compiling Objective-C natively.
+    // See Clang's `CGObjCCommonMac::EmitImageInfo`:
+    // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085
+    pub(crate) fn add_objc_module_flags(&self) {
+        let abi_version = self.objc_abi_version();
+
+        llvm::add_module_flag_u32(
+            self.llmod,
+            llvm::ModuleFlagMergeBehavior::Error,
+            "Objective-C Version",
+            abi_version,
+        );
+
+        llvm::add_module_flag_u32(
+            self.llmod,
+            llvm::ModuleFlagMergeBehavior::Error,
+            "Objective-C Image Info Version",
+            0,
+        );
+
+        llvm::add_module_flag_str(
+            self.llmod,
+            llvm::ModuleFlagMergeBehavior::Error,
+            "Objective-C Image Info Section",
+            match abi_version {
+                1 => "__OBJC,__image_info,regular",
+                2 => "__DATA,__objc_imageinfo,regular,no_dead_strip",
+                _ => unreachable!(),
+            },
+        );
+
+        if self.tcx.sess.target.env == "sim" {
+            llvm::add_module_flag_u32(
+                self.llmod,
+                llvm::ModuleFlagMergeBehavior::Error,
+                "Objective-C Is Simulated",
+                1 << 5,
+            );
+        }
+
+        llvm::add_module_flag_u32(
+            self.llmod,
+            llvm::ModuleFlagMergeBehavior::Error,
+            "Objective-C Class Properties",
+            1 << 6,
+        );
+    }
 }
 impl<'ll> SimpleCx<'ll> {
     pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index dc3a84b6a15..0903ddab285 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -32,9 +32,7 @@ use self::type_map::{DINodeCreationResult, Stub, UniqueTypeId};
 use super::CodegenUnitDebugContext;
 use super::namespace::mangled_name_of_instance;
 use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name};
-use super::utils::{
-    DIB, create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit,
-};
+use super::utils::{DIB, debug_context, get_namespace_for_item, is_node_local_to_unit};
 use crate::common::{AsCCharPtr, CodegenCx};
 use crate::debuginfo::dwarf_const;
 use crate::debuginfo::metadata::type_map::build_type_with_children;
@@ -119,17 +117,17 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>(
         .try_to_target_usize(cx.tcx)
         .expect("expected monomorphic const in codegen") as c_longlong;
 
-    let subrange =
-        unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) };
+    let subrange = unsafe { llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) };
+    let subscripts = &[subrange];
 
-    let subscripts = create_DIArray(DIB(cx), &[subrange]);
     let di_node = unsafe {
-        llvm::LLVMRustDIBuilderCreateArrayType(
+        llvm::LLVMDIBuilderCreateArrayType(
             DIB(cx),
             size.bits(),
             align.bits() as u32,
             element_type_di_node,
-            subscripts,
+            subscripts.as_ptr(),
+            subscripts.len() as c_uint,
         )
     };
 
@@ -175,17 +173,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
                 "ptr_type={ptr_type}, pointee_type={pointee_type}",
             );
 
-            let di_node = unsafe {
-                llvm::LLVMRustDIBuilderCreatePointerType(
-                    DIB(cx),
-                    pointee_type_di_node,
-                    pointer_size.bits(),
-                    pointer_align.abi.bits() as u32,
-                    0, // Ignore DWARF address space.
-                    ptr_type_debuginfo_name.as_c_char_ptr(),
-                    ptr_type_debuginfo_name.len(),
-                )
-            };
+            let di_node = create_pointer_type(
+                cx,
+                pointee_type_di_node,
+                pointer_size,
+                pointer_align.abi,
+                &ptr_type_debuginfo_name,
+            );
 
             DINodeCreationResult { di_node, already_stored_in_typemap: false }
         }
@@ -233,17 +227,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
 
                     // The data pointer type is a regular, thin pointer, regardless of whether this
                     // is a slice or a trait object.
-                    let data_ptr_type_di_node = unsafe {
-                        llvm::LLVMRustDIBuilderCreatePointerType(
-                            DIB(cx),
-                            pointee_type_di_node,
-                            addr_field.size.bits(),
-                            addr_field.align.abi.bits() as u32,
-                            0, // Ignore DWARF address space.
-                            std::ptr::null(),
-                            0,
-                        )
-                    };
+                    let data_ptr_type_di_node = create_pointer_type(
+                        cx,
+                        pointee_type_di_node,
+                        addr_field.size,
+                        addr_field.align.abi,
+                        "",
+                    );
 
                     smallvec![
                         build_field_di_node(
@@ -318,7 +308,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>(
 
     debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id);
 
-    let fn_di_node = create_subroutine_type(cx, create_DIArray(DIB(cx), &signature_di_nodes[..]));
+    let fn_di_node = create_subroutine_type(cx, &signature_di_nodes[..]);
 
     // This is actually a function pointer, so wrap it in pointer DI.
     let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false);
@@ -329,26 +319,44 @@ fn build_subroutine_type_di_node<'ll, 'tcx>(
         }
         _ => unreachable!(),
     };
-    let di_node = unsafe {
-        llvm::LLVMRustDIBuilderCreatePointerType(
-            DIB(cx),
-            fn_di_node,
-            size.bits(),
-            align.bits() as u32,
-            0, // Ignore DWARF address space.
-            name.as_c_char_ptr(),
-            name.len(),
-        )
-    };
+    let di_node = create_pointer_type(cx, fn_di_node, size, align, &name);
 
     DINodeCreationResult::new(di_node, false)
 }
 
 pub(super) fn create_subroutine_type<'ll>(
     cx: &CodegenCx<'ll, '_>,
-    signature: &'ll DICompositeType,
+    signature: &[Option<&'ll llvm::Metadata>],
 ) -> &'ll DICompositeType {
-    unsafe { llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), signature) }
+    unsafe {
+        llvm::LLVMDIBuilderCreateSubroutineType(
+            DIB(cx),
+            None, // ("File" is ignored and has no effect)
+            signature.as_ptr(),
+            signature.len() as c_uint,
+            DIFlags::FlagZero, // (default value)
+        )
+    }
+}
+
+fn create_pointer_type<'ll>(
+    cx: &CodegenCx<'ll, '_>,
+    pointee_ty: &'ll llvm::Metadata,
+    size: Size,
+    align: Align,
+    name: &str,
+) -> &'ll llvm::Metadata {
+    unsafe {
+        llvm::LLVMDIBuilderCreatePointerType(
+            DIB(cx),
+            pointee_ty,
+            size.bits(),
+            align.bits() as u32,
+            0, // Ignore DWARF address space.
+            name.as_ptr(),
+            name.len(),
+        )
+    }
 }
 
 /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs
@@ -834,12 +842,13 @@ fn create_basic_type<'ll, 'tcx>(
     encoding: u32,
 ) -> &'ll DIBasicType {
     unsafe {
-        llvm::LLVMRustDIBuilderCreateBasicType(
+        llvm::LLVMDIBuilderCreateBasicType(
             DIB(cx),
-            name.as_c_char_ptr(),
+            name.as_ptr(),
             name.len(),
             size.bits(),
             encoding,
+            DIFlags::FlagZero,
         )
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
index 18a783a348a..37200fdc41a 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
@@ -1,5 +1,6 @@
 use std::cell::RefCell;
 
+use libc::c_uint;
 use rustc_abi::{Align, Size, VariantIdx};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::FxHashMap;
@@ -9,7 +10,7 @@ use rustc_middle::bug;
 use rustc_middle::ty::{self, ExistentialTraitRef, Ty, TyCtxt};
 
 use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata};
-use crate::common::{AsCCharPtr, CodegenCx};
+use crate::common::CodegenCx;
 use crate::debuginfo::utils::{DIB, create_DIArray, debug_context};
 use crate::llvm::debuginfo::{DIFlags, DIScope, DIType};
 use crate::llvm::{self};
@@ -191,7 +192,7 @@ pub(super) fn stub<'ll, 'tcx>(
     containing_scope: Option<&'ll DIScope>,
     flags: DIFlags,
 ) -> StubInfo<'ll, 'tcx> {
-    let empty_array = create_DIArray(DIB(cx), &[]);
+    let no_elements: &[Option<&llvm::Metadata>] = &[];
     let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
 
     let (file_metadata, line_number) = if let Some(def_location) = def_location {
@@ -207,10 +208,10 @@ pub(super) fn stub<'ll, 'tcx>(
                 _ => None,
             };
             unsafe {
-                llvm::LLVMRustDIBuilderCreateStructType(
+                llvm::LLVMDIBuilderCreateStructType(
                     DIB(cx),
                     containing_scope,
-                    name.as_c_char_ptr(),
+                    name.as_ptr(),
                     name.len(),
                     file_metadata,
                     line_number,
@@ -218,28 +219,30 @@ pub(super) fn stub<'ll, 'tcx>(
                     align.bits() as u32,
                     flags,
                     None,
-                    empty_array,
-                    0,
+                    no_elements.as_ptr(),
+                    no_elements.len() as c_uint,
+                    0u32, // (Objective-C runtime version; default is 0)
                     vtable_holder,
-                    unique_type_id_str.as_c_char_ptr(),
+                    unique_type_id_str.as_ptr(),
                     unique_type_id_str.len(),
                 )
             }
         }
         Stub::Union => unsafe {
-            llvm::LLVMRustDIBuilderCreateUnionType(
+            llvm::LLVMDIBuilderCreateUnionType(
                 DIB(cx),
                 containing_scope,
-                name.as_c_char_ptr(),
+                name.as_ptr(),
                 name.len(),
                 file_metadata,
                 line_number,
                 size.bits(),
                 align.bits() as u32,
                 flags,
-                Some(empty_array),
-                0,
-                unique_type_id_str.as_c_char_ptr(),
+                no_elements.as_ptr(),
+                no_elements.len() as c_uint,
+                0u32, // (Objective-C runtime version; default is 0)
+                unique_type_id_str.as_ptr(),
                 unique_type_id_str.len(),
             )
         },
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 79334f7f9fe..126082aa3aa 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -349,7 +349,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         let file_metadata = file_metadata(self, &loc.file);
 
         let function_type_metadata =
-            create_subroutine_type(self, get_function_signature(self, fn_abi));
+            create_subroutine_type(self, &get_function_signature(self, fn_abi));
 
         let mut name = String::with_capacity(64);
         type_names::push_item_name(tcx, def_id, false, &mut name);
@@ -441,9 +441,9 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         fn get_function_signature<'ll, 'tcx>(
             cx: &CodegenCx<'ll, 'tcx>,
             fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
-        ) -> &'ll DIArray {
+        ) -> Vec<Option<&'ll llvm::Metadata>> {
             if cx.sess().opts.debuginfo != DebugInfo::Full {
-                return create_DIArray(DIB(cx), &[]);
+                return vec![];
             }
 
             let mut signature = Vec::with_capacity(fn_abi.args.len() + 1);
@@ -484,7 +484,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                     .extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty))));
             }
 
-            create_DIArray(DIB(cx), &signature[..])
+            signature
         }
 
         fn get_template_parameters<'ll, 'tcx>(
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 0679f55ab7f..71d8b7d25fe 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -24,9 +24,9 @@ use rustc_target::spec::SymbolVisibility;
 
 use super::RustString;
 use super::debuginfo::{
-    DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator,
-    DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram,
-    DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind,
+    DIArray, DIBasicType, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags,
+    DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange,
+    DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind,
 };
 use crate::llvm;
 
@@ -1237,6 +1237,7 @@ unsafe extern "C" {
     pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool);
     pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
     pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind);
+    pub(crate) safe fn LLVMSetExternallyInitialized(GlobalVar: &Value, IsExtInit: Bool);
 
     // Operations on attributes
     pub(crate) fn LLVMCreateStringAttribute(
@@ -1870,6 +1871,78 @@ unsafe extern "C" {
         Scope: &'ll Metadata,
         InlinedAt: Option<&'ll Metadata>,
     ) -> &'ll Metadata;
+
+    pub(crate) fn LLVMDIBuilderCreateSubroutineType<'ll>(
+        Builder: &DIBuilder<'ll>,
+        File: Option<&'ll Metadata>, // (ignored and has no effect)
+        ParameterTypes: *const Option<&'ll Metadata>,
+        NumParameterTypes: c_uint,
+        Flags: DIFlags, // (default is `DIFlags::DIFlagZero`)
+    ) -> &'ll Metadata;
+
+    pub(crate) fn LLVMDIBuilderCreateUnionType<'ll>(
+        Builder: &DIBuilder<'ll>,
+        Scope: Option<&'ll Metadata>,
+        Name: *const c_uchar, // See "PTR_LEN_STR".
+        NameLen: size_t,
+        File: &'ll Metadata,
+        LineNumber: c_uint,
+        SizeInBits: u64,
+        AlignInBits: u32,
+        Flags: DIFlags,
+        Elements: *const Option<&'ll Metadata>,
+        NumElements: c_uint,
+        RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0)
+        UniqueId: *const c_uchar, // See "PTR_LEN_STR".
+        UniqueIdLen: size_t,
+    ) -> &'ll Metadata;
+
+    pub(crate) fn LLVMDIBuilderCreateArrayType<'ll>(
+        Builder: &DIBuilder<'ll>,
+        Size: u64,
+        Align: u32,
+        Ty: &'ll Metadata,
+        Subscripts: *const &'ll Metadata,
+        NumSubscripts: c_uint,
+    ) -> &'ll Metadata;
+
+    pub(crate) fn LLVMDIBuilderCreateBasicType<'ll>(
+        Builder: &DIBuilder<'ll>,
+        Name: *const c_uchar, // See "PTR_LEN_STR".
+        NameLen: size_t,
+        SizeInBits: u64,
+        Encoding: c_uint, // (`LLVMDWARFTypeEncoding`)
+        Flags: DIFlags,   // (default is `DIFlags::DIFlagZero`)
+    ) -> &'ll Metadata;
+
+    pub(crate) fn LLVMDIBuilderCreatePointerType<'ll>(
+        Builder: &DIBuilder<'ll>,
+        PointeeTy: &'ll Metadata,
+        SizeInBits: u64,
+        AlignInBits: u32,
+        AddressSpace: c_uint, // (optional DWARF address space; default is 0)
+        Name: *const c_uchar, // See "PTR_LEN_STR".
+        NameLen: size_t,
+    ) -> &'ll Metadata;
+
+    pub(crate) fn LLVMDIBuilderCreateStructType<'ll>(
+        Builder: &DIBuilder<'ll>,
+        Scope: Option<&'ll Metadata>,
+        Name: *const c_uchar, // See "PTR_LEN_STR".
+        NameLen: size_t,
+        File: &'ll Metadata,
+        LineNumber: c_uint,
+        SizeInBits: u64,
+        AlignInBits: u32,
+        Flags: DIFlags,
+        DerivedFrom: Option<&'ll Metadata>,
+        Elements: *const Option<&'ll Metadata>,
+        NumElements: c_uint,
+        RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0)
+        VTableHolder: Option<&'ll Metadata>,
+        UniqueId: *const c_uchar, // See "PTR_LEN_STR".
+        UniqueIdLen: size_t,
+    ) -> &'ll Metadata;
 }
 
 #[link(name = "llvm-wrapper", kind = "static")]
@@ -2172,11 +2245,6 @@ unsafe extern "C" {
         SourceLen: size_t,
     ) -> &'a DIFile;
 
-    pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>(
-        Builder: &DIBuilder<'a>,
-        ParameterTypes: &'a DIArray,
-    ) -> &'a DICompositeType;
-
     pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>(
         Builder: &DIBuilder<'a>,
         Scope: &'a DIDescriptor,
@@ -2210,14 +2278,6 @@ unsafe extern "C" {
         TParam: &'a DIArray,
     ) -> &'a DISubprogram;
 
-    pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>(
-        Builder: &DIBuilder<'a>,
-        Name: *const c_char,
-        NameLen: size_t,
-        SizeInBits: u64,
-        Encoding: c_uint,
-    ) -> &'a DIBasicType;
-
     pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>(
         Builder: &DIBuilder<'a>,
         Type: &'a DIBasicType,
@@ -2228,34 +2288,6 @@ unsafe extern "C" {
         Scope: Option<&'a DIScope>,
     ) -> &'a DIDerivedType;
 
-    pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>(
-        Builder: &DIBuilder<'a>,
-        PointeeTy: &'a DIType,
-        SizeInBits: u64,
-        AlignInBits: u32,
-        AddressSpace: c_uint,
-        Name: *const c_char,
-        NameLen: size_t,
-    ) -> &'a DIDerivedType;
-
-    pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>(
-        Builder: &DIBuilder<'a>,
-        Scope: Option<&'a DIDescriptor>,
-        Name: *const c_char,
-        NameLen: size_t,
-        File: &'a DIFile,
-        LineNumber: c_uint,
-        SizeInBits: u64,
-        AlignInBits: u32,
-        Flags: DIFlags,
-        DerivedFrom: Option<&'a DIType>,
-        Elements: &'a DIArray,
-        RunTimeLang: c_uint,
-        VTableHolder: Option<&'a DIType>,
-        UniqueId: *const c_char,
-        UniqueIdLen: size_t,
-    ) -> &'a DICompositeType;
-
     pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>(
         Builder: &DIBuilder<'a>,
         Scope: &'a DIDescriptor,
@@ -2335,14 +2367,6 @@ unsafe extern "C" {
         AlignInBits: u32,
     ) -> &'a DIVariable;
 
-    pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>(
-        Builder: &DIBuilder<'a>,
-        Size: u64,
-        AlignInBits: u32,
-        Ty: &'a DIType,
-        Subscripts: &'a DIArray,
-    ) -> &'a DIType;
-
     pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>(
         Builder: &DIBuilder<'a>,
         Lo: i64,
@@ -2388,22 +2412,6 @@ unsafe extern "C" {
         IsScoped: bool,
     ) -> &'a DIType;
 
-    pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>(
-        Builder: &DIBuilder<'a>,
-        Scope: Option<&'a DIScope>,
-        Name: *const c_char,
-        NameLen: size_t,
-        File: &'a DIFile,
-        LineNumber: c_uint,
-        SizeInBits: u64,
-        AlignInBits: u32,
-        Flags: DIFlags,
-        Elements: Option<&'a DIArray>,
-        RunTimeLang: c_uint,
-        UniqueId: *const c_char,
-        UniqueIdLen: size_t,
-    ) -> &'a DIType;
-
     pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>(
         Builder: &DIBuilder<'a>,
         Scope: &'a DIScope,
diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
index d6974e22c85..1115d82fa85 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs
@@ -258,6 +258,10 @@ pub(crate) fn set_alignment(llglobal: &Value, align: Align) {
     }
 }
 
+pub(crate) fn set_externally_initialized(llglobal: &Value, is_ext_init: bool) {
+    LLVMSetExternallyInitialized(llglobal, is_ext_init.to_llvm_bool());
+}
+
 /// Get the `name`d comdat from `llmod` and assign it to `llglobal`.
 ///
 /// Inserts the comdat into `llmod` if it does not exist.
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index d927ffd78c2..8461c8b03d5 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -246,9 +246,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
         ("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")),
         ("aarch64", "paca") => Some(LLVMFeature::new("pauth")),
         ("aarch64", "pacg") => Some(LLVMFeature::new("pauth")),
-        // Before LLVM 20 those two features were packaged together as b16b16
-        ("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
-        ("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")),
         ("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")),
         // Rust ties fp and neon together.
         ("aarch64", "neon") => Some(LLVMFeature::with_dependencies(
@@ -262,57 +259,17 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
         // Filter out features that are not supported by the current LLVM version
         ("aarch64", "fpmr") => None, // only existed in 18
         ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")),
-        // NVPTX targets added in LLVM 20
-        ("nvptx64", "sm_100") if get_version().0 < 20 => None,
-        ("nvptx64", "sm_100a") if get_version().0 < 20 => None,
-        ("nvptx64", "sm_101") if get_version().0 < 20 => None,
-        ("nvptx64", "sm_101a") if get_version().0 < 20 => None,
-        ("nvptx64", "sm_120") if get_version().0 < 20 => None,
-        ("nvptx64", "sm_120a") if get_version().0 < 20 => None,
-        ("nvptx64", "ptx86") if get_version().0 < 20 => None,
-        ("nvptx64", "ptx87") if get_version().0 < 20 => None,
         // Filter out features that are not supported by the current LLVM version
-        ("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq")
-            if get_version().0 < 20 =>
-        {
-            None
-        }
         ("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None,
-        // Filter out features that are not supported by the current LLVM version
-        ("riscv32" | "riscv64", "zacas" | "rva23u64" | "supm") if get_version().0 < 20 => None,
-        (
-            "s390x",
-            "message-security-assist-extension12"
-            | "concurrent-functions"
-            | "miscellaneous-extensions-4"
-            | "vector-enhancements-3"
-            | "vector-packed-decimal-enhancement-3",
-        ) if get_version().0 < 20 => None,
         // Enable the evex512 target feature if an avx512 target feature is enabled.
         ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies(
             s,
             smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")],
         )),
-        // Support for `wide-arithmetic` will first land in LLVM 20 as part of
-        // llvm/llvm-project#111598
-        ("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None,
         ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")),
-        // In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used".
-        // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28
-        // Before LLVM 19, there was no `v8plus` feature and `v9` means "SPARC-V9 instruction available".
-        // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26
-        ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")),
         ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")),
-        // These new `amx` variants and `movrs` were introduced in LLVM20
-        ("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose")
-            if get_version().0 < 20 =>
-        {
-            None
-        }
-        ("x86", "movrs") if get_version().0 < 20 => None,
         ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")),
-        ("x86", "avx10.2") if get_version().0 < 20 => None,
-        ("x86", "avx10.2") if get_version().0 >= 20 => Some(LLVMFeature::new("avx10.2-512")),
+        ("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")),
         ("x86", "apxf") => Some(LLVMFeature::with_dependencies(
             "egpr",
             smallvec![
@@ -716,17 +673,7 @@ pub(crate) fn global_llvm_features(
     };
 
     // Features implied by an implicit or explicit `--target`.
-    features.extend(
-        sess.target
-            .features
-            .split(',')
-            .filter(|v| !v.is_empty())
-            // Drop +v8plus feature introduced in LLVM 20.
-            // (Hard-coded target features do not go through `to_llvm_feature` since they already
-            // are LLVM feature names, hence we need a special case here.)
-            .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0))
-            .map(String::from),
-    );
+    features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));
 
     if wants_wasm_eh(sess) && sess.panic_strategy() == PanicStrategy::Unwind {
         features.push("+exception-handling".into());