about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_ssa')
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs91
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs16
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs551
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/write.rs6
7 files changed, 336 insertions, 346 deletions
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index cfae1b3ec98..94501da69a7 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -8,8 +8,8 @@ edition = "2024"
 ar_archive_writer = "0.4.2"
 bitflags = "2.4.1"
 bstr = "1.11.3"
-# Pinned so `cargo update` bumps don't cause breakage. Please also update the
-# `cc` in `rustc_llvm` if you update the `cc` here.
+# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version
+# per crate", so if you change this, you need to also change it in `rustc_llvm`.
 cc = "=1.2.16"
 itertools = "0.12"
 pathdiff = "0.2.0"
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 5ce301c0eb9..162fbf3d6e2 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -3369,12 +3369,12 @@ fn warn_if_linked_with_gold(sess: &Session, path: &Path) -> Result<(), Box<dyn s
 
         let section =
             elf.sections(endian, data)?.section_by_name(endian, b".note.gnu.gold-version");
-        if let Some((_, section)) = section {
-            if let Some(mut notes) = section.notes(endian, data)? {
-                return Ok(notes.any(|note| {
-                    note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION)
-                }));
-            }
+        if let Some((_, section)) = section
+            && let Some(mut notes) = section.notes(endian, data)?
+        {
+            return Ok(notes.any(|note| {
+                note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION)
+            }));
         }
 
         Ok(false)
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 2f5eca2d6b2..297bdec2bc2 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -86,7 +86,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
             // Only consider nodes that actually have exported symbols.
             match tcx.def_kind(def_id) {
                 DefKind::Fn | DefKind::Static { .. } => {}
-                DefKind::AssocFn if tcx.impl_of_method(def_id.to_def_id()).is_some() => {}
+                DefKind::AssocFn if tcx.impl_of_assoc(def_id.to_def_id()).is_some() => {}
                 _ => return None,
             };
 
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 7be274df1d4..6773d3e24e9 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -1,4 +1,3 @@
-use std::any::Any;
 use std::assert_matches::assert_matches;
 use std::marker::PhantomData;
 use std::path::{Path, PathBuf};
@@ -372,8 +371,6 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     /// The incremental compilation session directory, or None if we are not
     /// compiling incrementally
     pub incr_comp_session_dir: Option<PathBuf>,
-    /// Channel back to the main control thread to send messages to
-    pub coordinator_send: Sender<Box<dyn Any + Send>>,
     /// `true` if the codegen should be run in parallel.
     ///
     /// Depends on [`ExtraBackendMethods::supports_parallel()`] and `-Zno_parallel_backend`.
@@ -800,10 +797,6 @@ pub(crate) enum WorkItemResult<B: WriteBackendMethods> {
     /// The backend has finished compiling a CGU, nothing more required.
     Finished(CompiledModule),
 
-    /// The backend has finished compiling a CGU, which now needs linking
-    /// because `-Zcombine-cgu` was specified.
-    NeedsLink(ModuleCodegen<B::Module>),
-
     /// The backend has finished compiling a CGU, which now needs to go through
     /// fat LTO.
     NeedsFatLto(FatLtoInput<B>),
@@ -887,7 +880,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
     };
 
     match lto_type {
-        ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config),
+        ComputedLtoType::No => {
+            let module = B::codegen(cgcx, module, module_config)?;
+            Ok(WorkItemResult::Finished(module))
+        }
         ComputedLtoType::Thin => {
             let (name, thin_buffer) = B::prepare_thin(module, false);
             if let Some(path) = bitcode {
@@ -1027,20 +1023,8 @@ fn execute_thin_lto_work_item<B: ExtraBackendMethods>(
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
     let module = B::optimize_thin(cgcx, module)?;
-    finish_intra_module_work(cgcx, module, module_config)
-}
-
-fn finish_intra_module_work<B: ExtraBackendMethods>(
-    cgcx: &CodegenContext<B>,
-    module: ModuleCodegen<B::Module>,
-    module_config: &ModuleConfig,
-) -> Result<WorkItemResult<B>, FatalError> {
-    if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator {
-        let module = B::codegen(cgcx, module, module_config)?;
-        Ok(WorkItemResult::Finished(module))
-    } else {
-        Ok(WorkItemResult::NeedsLink(module))
-    }
+    let module = B::codegen(cgcx, module, module_config)?;
+    Ok(WorkItemResult::Finished(module))
 }
 
 /// Messages sent to the coordinator.
@@ -1122,10 +1106,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
     autodiff_items: &[AutoDiffItem],
     shared_emitter: SharedEmitter,
     codegen_worker_send: Sender<CguMessage>,
-    coordinator_receive: Receiver<Box<dyn Any + Send>>,
+    coordinator_receive: Receiver<Message<B>>,
     regular_config: Arc<ModuleConfig>,
     allocator_config: Arc<ModuleConfig>,
-    tx_to_llvm_workers: Sender<Box<dyn Any + Send>>,
+    tx_to_llvm_workers: Sender<Message<B>>,
 ) -> thread::JoinHandle<Result<CompiledModules, ()>> {
     let coordinator_send = tx_to_llvm_workers;
     let sess = tcx.sess;
@@ -1153,7 +1137,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
     let coordinator_send2 = coordinator_send.clone();
     let helper = jobserver::client()
         .into_helper_thread(move |token| {
-            drop(coordinator_send2.send(Box::new(Message::Token::<B>(token))));
+            drop(coordinator_send2.send(Message::Token::<B>(token)));
         })
         .expect("failed to spawn helper thread");
 
@@ -1187,7 +1171,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
         remark: sess.opts.cg.remark.clone(),
         remark_dir,
         incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
-        coordinator_send,
         expanded_args: tcx.sess.expanded_args.clone(),
         diag_emitter: shared_emitter.clone(),
         output_filenames: Arc::clone(tcx.output_filenames(())),
@@ -1347,7 +1330,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
         // through codegen and LLVM.
         let mut compiled_modules = vec![];
         let mut compiled_allocator_module = None;
-        let mut needs_link = Vec::new();
         let mut needs_fat_lto = Vec::new();
         let mut needs_thin_lto = Vec::new();
         let mut lto_import_only_modules = Vec::new();
@@ -1423,7 +1405,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
                         let (item, _) =
                             work_items.pop().expect("queue empty - queue_full_enough() broken?");
                         main_thread_state = MainThreadState::Lending;
-                        spawn_work(&cgcx, &mut llvm_start_time, item);
+                        spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
                     }
                 }
             } else if codegen_state == Completed {
@@ -1502,7 +1484,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
                     MainThreadState::Idle => {
                         if let Some((item, _)) = work_items.pop() {
                             main_thread_state = MainThreadState::Lending;
-                            spawn_work(&cgcx, &mut llvm_start_time, item);
+                            spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
                         } else {
                             // There is no unstarted work, so let the main thread
                             // take over for a running worker. Otherwise the
@@ -1538,7 +1520,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
                 while running_with_own_token < tokens.len()
                     && let Some((item, _)) = work_items.pop()
                 {
-                    spawn_work(&cgcx, &mut llvm_start_time, item);
+                    spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item);
                     running_with_own_token += 1;
                 }
             }
@@ -1546,8 +1528,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
             // Relinquish accidentally acquired extra tokens.
             tokens.truncate(running_with_own_token);
 
-            let msg = coordinator_receive.recv().unwrap();
-            match *msg.downcast::<Message<B>>().ok().unwrap() {
+            match coordinator_receive.recv().unwrap() {
                 // Save the token locally and the next turn of the loop will use
                 // this to spawn a new unit of work, or it may get dropped
                 // immediately if we have no more work to spawn.
@@ -1630,7 +1611,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
                         Ok(WorkItemResult::Finished(compiled_module)) => {
                             match compiled_module.kind {
                                 ModuleKind::Regular => {
-                                    assert!(needs_link.is_empty());
                                     compiled_modules.push(compiled_module);
                                 }
                                 ModuleKind::Allocator => {
@@ -1639,10 +1619,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
                                 }
                             }
                         }
-                        Ok(WorkItemResult::NeedsLink(module)) => {
-                            assert!(compiled_modules.is_empty());
-                            needs_link.push(module);
-                        }
                         Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => {
                             assert!(!started_lto);
                             assert!(needs_thin_lto.is_empty());
@@ -1679,17 +1655,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
             return Err(());
         }
 
-        let needs_link = mem::take(&mut needs_link);
-        if !needs_link.is_empty() {
-            assert!(compiled_modules.is_empty());
-            let dcx = cgcx.create_dcx();
-            let dcx = dcx.handle();
-            let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?;
-            let module =
-                B::codegen(&cgcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?;
-            compiled_modules.push(module);
-        }
-
         // Drop to print timings
         drop(llvm_start_time);
 
@@ -1769,6 +1734,7 @@ pub(crate) struct WorkerFatalError;
 
 fn spawn_work<'a, B: ExtraBackendMethods>(
     cgcx: &'a CodegenContext<B>,
+    coordinator_send: Sender<Message<B>>,
     llvm_start_time: &mut Option<VerboseTimingGuard<'a>>,
     work: WorkItem<B>,
 ) {
@@ -1782,7 +1748,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
         // Set up a destructor which will fire off a message that we're done as
         // we exit.
         struct Bomb<B: ExtraBackendMethods> {
-            coordinator_send: Sender<Box<dyn Any + Send>>,
+            coordinator_send: Sender<Message<B>>,
             result: Option<Result<WorkItemResult<B>, FatalError>>,
         }
         impl<B: ExtraBackendMethods> Drop for Bomb<B> {
@@ -1794,11 +1760,11 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
                     }
                     None => Message::WorkItem::<B> { result: Err(None) },
                 };
-                drop(self.coordinator_send.send(Box::new(msg)));
+                drop(self.coordinator_send.send(msg));
             }
         }
 
-        let mut bomb = Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None };
+        let mut bomb = Bomb::<B> { coordinator_send, result: None };
 
         // Execute the work itself, and if it finishes successfully then flag
         // ourselves as a success as well.
@@ -2003,7 +1969,7 @@ impl SharedEmitterMain {
 }
 
 pub struct Coordinator<B: ExtraBackendMethods> {
-    pub sender: Sender<Box<dyn Any + Send>>,
+    sender: Sender<Message<B>>,
     future: Option<thread::JoinHandle<Result<CompiledModules, ()>>>,
     // Only used for the Message type.
     phantom: PhantomData<B>,
@@ -2020,7 +1986,7 @@ impl<B: ExtraBackendMethods> Drop for Coordinator<B> {
         if let Some(future) = self.future.take() {
             // If we haven't joined yet, signal to the coordinator that it should spawn no more
             // work, and wait for worker threads to finish.
-            drop(self.sender.send(Box::new(Message::CodegenAborted::<B>)));
+            drop(self.sender.send(Message::CodegenAborted::<B>));
             drop(future.join());
         }
     }
@@ -2079,7 +2045,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
     pub(crate) fn codegen_finished(&self, tcx: TyCtxt<'_>) {
         self.wait_for_signal_to_codegen_item();
         self.check_for_errors(tcx.sess);
-        drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>)));
+        drop(self.coordinator.sender.send(Message::CodegenComplete::<B>));
     }
 
     pub(crate) fn check_for_errors(&self, sess: &Session) {
@@ -2100,28 +2066,25 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
 }
 
 pub(crate) fn submit_codegened_module_to_llvm<B: ExtraBackendMethods>(
-    _backend: &B,
-    tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>,
+    coordinator: &Coordinator<B>,
     module: ModuleCodegen<B::Module>,
     cost: u64,
 ) {
     let llvm_work_item = WorkItem::Optimize(module);
-    drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost })));
+    drop(coordinator.sender.send(Message::CodegenDone::<B> { llvm_work_item, cost }));
 }
 
 pub(crate) fn submit_post_lto_module_to_llvm<B: ExtraBackendMethods>(
-    _backend: &B,
-    tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>,
+    coordinator: &Coordinator<B>,
     module: CachedModuleCodegen,
 ) {
     let llvm_work_item = WorkItem::CopyPostLtoArtifacts(module);
-    drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost: 0 })));
+    drop(coordinator.sender.send(Message::CodegenDone::<B> { llvm_work_item, cost: 0 }));
 }
 
 pub(crate) fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>(
-    _backend: &B,
     tcx: TyCtxt<'_>,
-    tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>,
+    coordinator: &Coordinator<B>,
     module: CachedModuleCodegen,
 ) {
     let filename = pre_lto_bitcode_filename(&module.name);
@@ -2135,10 +2098,10 @@ pub(crate) fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>(
         })
     };
     // Schedule the module to be loaded
-    drop(tx_to_llvm_workers.send(Box::new(Message::AddImportOnlyModule::<B> {
+    drop(coordinator.sender.send(Message::AddImportOnlyModule::<B> {
         module_data: SerializedModule::FromUncompressedFile(mmap),
         work_product: module.source,
-    })));
+    }));
 }
 
 fn pre_lto_bitcode_filename(module_name: &str) -> String {
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 833456abb8a..a5807c56e31 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -702,8 +702,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
         // These modules are generally cheap and won't throw off scheduling.
         let cost = 0;
         submit_codegened_module_to_llvm(
-            &backend,
-            &ongoing_codegen.coordinator.sender,
+            &ongoing_codegen.coordinator,
             ModuleCodegen::new_allocator(llmod_id, module_llvm),
             cost,
         );
@@ -800,18 +799,12 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
                 // compilation hang on post-monomorphization errors.
                 tcx.dcx().abort_if_errors();
 
-                submit_codegened_module_to_llvm(
-                    &backend,
-                    &ongoing_codegen.coordinator.sender,
-                    module,
-                    cost,
-                );
+                submit_codegened_module_to_llvm(&ongoing_codegen.coordinator, module, cost);
             }
             CguReuse::PreLto => {
                 submit_pre_lto_module_to_llvm(
-                    &backend,
                     tcx,
-                    &ongoing_codegen.coordinator.sender,
+                    &ongoing_codegen.coordinator,
                     CachedModuleCodegen {
                         name: cgu.name().to_string(),
                         source: cgu.previous_work_product(tcx),
@@ -820,8 +813,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
             }
             CguReuse::PostLto => {
                 submit_post_lto_module_to_llvm(
-                    &backend,
-                    &ongoing_codegen.coordinator.sender,
+                    &ongoing_codegen.coordinator,
                     CachedModuleCodegen {
                         name: cgu.name().to_string(),
                         source: cgu.previous_work_product(tcx),
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index dd49db26689..5c54bce6e03 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -4,12 +4,12 @@ use rustc_abi::{Align, ExternAbi};
 use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
 use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
 use rustc_attr_data_structures::{
-    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, UsedBy, find_attr,
+    AttributeKind, InlineAttr, InstructionSetAttr, UsedBy, find_attr,
 };
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
-use rustc_hir::{self as hir, LangItem, lang_items};
+use rustc_hir::{self as hir, Attribute, LangItem, lang_items};
 use rustc_middle::middle::codegen_fn_attrs::{
     CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
 };
@@ -53,77 +53,196 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
     }
 }
 
-fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
-    if cfg!(debug_assertions) {
-        let def_kind = tcx.def_kind(did);
-        assert!(
-            def_kind.has_codegen_attrs(),
-            "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
-        );
+/// In some cases, attributes are only valid on functions, but it's the `check_attr`
+/// pass that checks that they aren't used anywhere else, rather than this module.
+/// In these cases, we bail from performing further checks that are only meaningful for
+/// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
+/// report a delayed bug, just in case `check_attr` isn't doing its job.
+fn try_fn_sig<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    did: LocalDefId,
+    attr_span: Span,
+) -> Option<ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>>> {
+    use DefKind::*;
+
+    let def_kind = tcx.def_kind(did);
+    if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
+        Some(tcx.fn_sig(did))
+    } else {
+        tcx.dcx().span_delayed_bug(attr_span, "this attribute can only be applied to functions");
+        None
     }
+}
 
-    let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
-    let mut codegen_fn_attrs = CodegenFnAttrs::new();
-    if tcx.should_inherit_track_caller(did) {
-        codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
+// FIXME(jdonszelmann): remove when instruction_set becomes a parsed attr
+fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<InstructionSetAttr> {
+    let list = attr.meta_item_list()?;
+
+    match &list[..] {
+        [MetaItemInner::MetaItem(set)] => {
+            let segments = set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
+            match segments.as_slice() {
+                [sym::arm, sym::a32 | sym::t32] if !tcx.sess.target.has_thumb_interworking => {
+                    tcx.dcx().emit_err(errors::UnsupportedInstructionSet { span: attr.span() });
+                    None
+                }
+                [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
+                [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
+                _ => {
+                    tcx.dcx().emit_err(errors::InvalidInstructionSet { span: attr.span() });
+                    None
+                }
+            }
+        }
+        [] => {
+            tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
+            None
+        }
+        _ => {
+            tcx.dcx().emit_err(errors::MultipleInstructionSet { span: attr.span() });
+            None
+        }
+    }
+}
+
+// FIXME(jdonszelmann): remove when linkage becomes a parsed attr
+fn parse_linkage_attr(tcx: TyCtxt<'_>, did: LocalDefId, attr: &Attribute) -> Option<Linkage> {
+    let val = attr.value_str()?;
+    let linkage = linkage_by_name(tcx, did, val.as_str());
+    Some(linkage)
+}
+
+// FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr
+fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option<SanitizerSet> {
+    let list = attr.meta_item_list()?;
+    let mut sanitizer_set = SanitizerSet::empty();
+
+    for item in list.iter() {
+        match item.name() {
+            Some(sym::address) => {
+                sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
+            }
+            Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI,
+            Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI,
+            Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY,
+            Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG,
+            Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK,
+            Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD,
+            Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS,
+            _ => {
+                tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
+            }
+        }
     }
 
+    Some(sanitizer_set)
+}
+
+// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr
+fn parse_patchable_function_entry(
+    tcx: TyCtxt<'_>,
+    attr: &Attribute,
+) -> Option<PatchableFunctionEntry> {
+    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().emit_err(errors::ExpectedNameValuePair { span: item.span() });
+                continue;
+            };
+
+            let Some(name_value_lit) = meta_item.name_value_literal() else {
+                tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
+                continue;
+            };
+
+            let attrib_to_write = match meta_item.name() {
+                Some(sym::prefix_nops) => &mut prefix,
+                Some(sym::entry_nops) => &mut entry,
+                _ => {
+                    tcx.dcx().emit_err(errors::UnexpectedParameterName {
+                        span: item.span(),
+                        prefix_nops: sym::prefix_nops,
+                        entry_nops: sym::entry_nops,
+                    });
+                    continue;
+                }
+            };
+
+            let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
+                tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span });
+                continue;
+            };
+
+            let Ok(val) = val.get().try_into() else {
+                tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
+                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)))
+    })
+}
+
+/// Spans that are collected when processing built-in attributes,
+/// that are useful for emitting diagnostics later.
+#[derive(Default)]
+struct InterestingAttributeDiagnosticSpans {
+    link_ordinal: Option<Span>,
+    no_sanitize: Option<Span>,
+    inline: Option<Span>,
+    no_mangle: Option<Span>,
+}
+
+/// Process the builtin attrs ([`hir::Attribute`]) on the item.
+/// Many of them directly translate to codegen attrs.
+fn process_builtin_attrs(
+    tcx: TyCtxt<'_>,
+    did: LocalDefId,
+    attrs: &[Attribute],
+    codegen_fn_attrs: &mut CodegenFnAttrs,
+) -> InterestingAttributeDiagnosticSpans {
+    let mut interesting_spans = InterestingAttributeDiagnosticSpans::default();
+    let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
+
     // If our rustc version supports autodiff/enzyme, then we call our handler
     // to check for any `#[rustc_autodiff(...)]` attributes.
+    // FIXME(jdonszelmann): merge with loop below
     if cfg!(llvm_enzyme) {
         let ad = autodiff_attrs(tcx, did.into());
         codegen_fn_attrs.autodiff_item = ad;
     }
 
-    // When `no_builtins` is applied at the crate level, we should add the
-    // `no-builtins` attribute to each function to ensure it takes effect in LTO.
-    let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
-    let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
-    if no_builtins {
-        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
-    }
-
-    let rust_target_features = tcx.rust_target_features(LOCAL_CRATE);
-
-    let mut link_ordinal_span = None;
-    let mut no_sanitize_span = None;
-
     for attr in attrs.iter() {
-        // In some cases, attribute are only valid on functions, but it's the `check_attr`
-        // pass that check that they aren't used anywhere else, rather this module.
-        // In these cases, we bail from performing further checks that are only meaningful for
-        // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
-        // report a delayed bug, just in case `check_attr` isn't doing its job.
-        let fn_sig = |attr_span| {
-            use DefKind::*;
-
-            let def_kind = tcx.def_kind(did);
-            if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
-                Some(tcx.fn_sig(did))
-            } else {
-                tcx.dcx()
-                    .span_delayed_bug(attr_span, "this attribute can only be applied to functions");
-                None
-            }
-        };
-
         if let hir::Attribute::Parsed(p) = attr {
             match p {
                 AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
                 AttributeKind::ExportName { name, .. } => {
-                    codegen_fn_attrs.export_name = Some(*name);
+                    codegen_fn_attrs.export_name = Some(*name)
+                }
+                AttributeKind::Inline(inline, span) => {
+                    codegen_fn_attrs.inline = *inline;
+                    interesting_spans.inline = Some(*span);
                 }
                 AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
                 AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
                 AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
                 AttributeKind::LinkOrdinal { ordinal, span } => {
                     codegen_fn_attrs.link_ordinal = Some(*ordinal);
-                    link_ordinal_span = Some(*span);
+                    interesting_spans.link_ordinal = Some(*span);
                 }
                 AttributeKind::LinkSection { name, .. } => {
                     codegen_fn_attrs.link_section = Some(*name)
                 }
                 AttributeKind::NoMangle(attr_span) => {
+                    interesting_spans.no_mangle = Some(*attr_span);
                     if tcx.opt_item_name(did.to_def_id()).is_some() {
                         codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
                     } else {
@@ -137,6 +256,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         });
                     }
                 }
+                AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
                 AttributeKind::TargetFeature(features, attr_span) => {
                     let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
                         tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
@@ -184,7 +304,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     let is_closure = tcx.is_closure_like(did.to_def_id());
 
                     if !is_closure
-                        && let Some(fn_sig) = fn_sig(*attr_span)
+                        && let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span)
                         && fn_sig.skip_binder().abi() != ExternAbi::Rust
                     {
                         tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
@@ -232,155 +352,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             }
             sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
             sym::linkage => {
-                if let Some(val) = attr.value_str() {
-                    let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
-                    if tcx.is_foreign_item(did) {
-                        codegen_fn_attrs.import_linkage = linkage;
-
-                        if tcx.is_mutable_static(did.into()) {
-                            let mut diag = tcx.dcx().struct_span_err(
-                                attr.span(),
-                                "extern mutable statics are not allowed with `#[linkage]`",
-                            );
-                            diag.note(
-                                "marking the extern static mutable would allow changing which \
-                                 symbol the static references rather than make the target of the \
-                                 symbol mutable",
-                            );
-                            diag.emit();
-                        }
-                    } else {
-                        codegen_fn_attrs.linkage = linkage;
+                let linkage = parse_linkage_attr(tcx, did, attr);
+
+                if tcx.is_foreign_item(did) {
+                    codegen_fn_attrs.import_linkage = linkage;
+
+                    if tcx.is_mutable_static(did.into()) {
+                        let mut diag = tcx.dcx().struct_span_err(
+                            attr.span(),
+                            "extern mutable statics are not allowed with `#[linkage]`",
+                        );
+                        diag.note(
+                            "marking the extern static mutable would allow changing which \
+                            symbol the static references rather than make the target of the \
+                            symbol mutable",
+                        );
+                        diag.emit();
                     }
+                } else {
+                    codegen_fn_attrs.linkage = linkage;
                 }
             }
             sym::no_sanitize => {
-                no_sanitize_span = Some(attr.span());
-                if let Some(list) = attr.meta_item_list() {
-                    for item in list.iter() {
-                        match item.name() {
-                            Some(sym::address) => {
-                                codegen_fn_attrs.no_sanitize |=
-                                    SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS
-                            }
-                            Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI,
-                            Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI,
-                            Some(sym::memory) => {
-                                codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY
-                            }
-                            Some(sym::memtag) => {
-                                codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG
-                            }
-                            Some(sym::shadow_call_stack) => {
-                                codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK
-                            }
-                            Some(sym::thread) => {
-                                codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD
-                            }
-                            Some(sym::hwaddress) => {
-                                codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
-                            }
-                            _ => {
-                                tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() });
-                            }
-                        }
-                    }
-                }
+                interesting_spans.no_sanitize = Some(attr.span());
+                codegen_fn_attrs.no_sanitize |=
+                    parse_no_sanitize_attr(tcx, attr).unwrap_or_default();
             }
             sym::instruction_set => {
-                codegen_fn_attrs.instruction_set =
-                    attr.meta_item_list().and_then(|l| match &l[..] {
-                        [MetaItemInner::MetaItem(set)] => {
-                            let segments =
-                                set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
-                            match segments.as_slice() {
-                                [sym::arm, sym::a32 | sym::t32]
-                                    if !tcx.sess.target.has_thumb_interworking =>
-                                {
-                                    tcx.dcx().emit_err(errors::UnsupportedInstructionSet {
-                                        span: attr.span(),
-                                    });
-                                    None
-                                }
-                                [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32),
-                                [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32),
-                                _ => {
-                                    tcx.dcx().emit_err(errors::InvalidInstructionSet {
-                                        span: attr.span(),
-                                    });
-                                    None
-                                }
-                            }
-                        }
-                        [] => {
-                            tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() });
-                            None
-                        }
-                        _ => {
-                            tcx.dcx()
-                                .emit_err(errors::MultipleInstructionSet { span: attr.span() });
-                            None
-                        }
-                    })
+                codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr)
             }
             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().emit_err(errors::ExpectedNameValuePair { span: item.span() });
-                            continue;
-                        };
-
-                        let Some(name_value_lit) = meta_item.name_value_literal() else {
-                            tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() });
-                            continue;
-                        };
-
-                        let attrib_to_write = match meta_item.name() {
-                            Some(sym::prefix_nops) => &mut prefix,
-                            Some(sym::entry_nops) => &mut entry,
-                            _ => {
-                                tcx.dcx().emit_err(errors::UnexpectedParameterName {
-                                    span: item.span(),
-                                    prefix_nops: sym::prefix_nops,
-                                    entry_nops: sym::entry_nops,
-                                });
-                                continue;
-                            }
-                        };
-
-                        let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
-                            tcx.dcx().emit_err(errors::InvalidLiteralValue {
-                                span: name_value_lit.span,
-                            });
-                            continue;
-                        };
-
-                        let Ok(val) = val.get().try_into() else {
-                            tcx.dcx()
-                                .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span });
-                            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),
-                    ))
-                })
+                codegen_fn_attrs.patchable_function_entry =
+                    parse_patchable_function_entry(tcx, attr);
             }
             _ => {}
         }
     }
 
+    interesting_spans
+}
+
+/// Applies overrides for codegen fn attrs. These often have a specific reason why they're necessary.
+/// Please comment why when adding a new one!
+fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut CodegenFnAttrs) {
     // Apply the minimum function alignment here. This ensures that a function's alignment is
     // determined by the `-C` flags of the crate it is defined in, not the `-C` flags of the crate
     // it happens to be codegen'd (or const-eval'd) in.
@@ -390,15 +404,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     // On trait methods, inherit the `#[align]` of the trait's method prototype.
     codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did));
 
-    let inline_span;
-    (codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) =
-        find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span))
-    {
-        (inline_attr, Some(span))
-    } else {
-        (InlineAttr::None, None)
-    };
-
     // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself,
     // but not for the code generation backend because at that point the naked function will just be
     // a declaration, with a definition provided in global assembly.
@@ -406,9 +411,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         codegen_fn_attrs.inline = InlineAttr::Never;
     }
 
-    codegen_fn_attrs.optimize =
-        find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
-
     // #73631: closures inherit `#[target_feature]` annotations
     //
     // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
@@ -431,6 +433,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         }
     }
 
+    // When `no_builtins` is applied at the crate level, we should add the
+    // `no-builtins` attribute to each function to ensure it takes effect in LTO.
+    let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
+    let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins);
+    if no_builtins {
+        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
+    }
+
+    // inherit track-caller properly
+    if tcx.should_inherit_track_caller(did) {
+        codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
+    }
+}
+
+fn check_result(
+    tcx: TyCtxt<'_>,
+    did: LocalDefId,
+    interesting_spans: InterestingAttributeDiagnosticSpans,
+    codegen_fn_attrs: &CodegenFnAttrs,
+) {
     // If a function uses `#[target_feature]` it can't be inlined into general
     // purpose functions as they wouldn't have the right target features
     // enabled. For that reason we also forbid `#[inline(always)]` as it can't be
@@ -446,14 +468,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     // llvm/llvm-project#70563).
     if !codegen_fn_attrs.target_features.is_empty()
         && matches!(codegen_fn_attrs.inline, InlineAttr::Always)
-        && let Some(span) = inline_span
+        && let Some(span) = interesting_spans.inline
     {
         tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`");
     }
 
+    // warn that inline has no effect when no_sanitize is present
     if !codegen_fn_attrs.no_sanitize.is_empty()
         && codegen_fn_attrs.inline.always()
-        && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span)
+        && let (Some(no_sanitize_span), Some(inline_span)) =
+            (interesting_spans.no_sanitize, interesting_spans.inline)
     {
         let hir_id = tcx.local_def_id_to_hir_id(did);
         tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
@@ -462,6 +486,47 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         })
     }
 
+    // error when specifying link_name together with link_ordinal
+    if let Some(_) = codegen_fn_attrs.link_name
+        && let Some(_) = codegen_fn_attrs.link_ordinal
+    {
+        let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
+        if let Some(span) = interesting_spans.link_ordinal {
+            tcx.dcx().span_err(span, msg);
+        } else {
+            tcx.dcx().err(msg);
+        }
+    }
+
+    if let Some(features) = check_tied_features(
+        tcx.sess,
+        &codegen_fn_attrs
+            .target_features
+            .iter()
+            .map(|features| (features.name.as_str(), true))
+            .collect(),
+    ) {
+        let span =
+            find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span)
+                .unwrap_or_else(|| tcx.def_span(did));
+
+        tcx.dcx()
+            .create_err(errors::TargetFeatureDisableOrEnable {
+                features,
+                span: Some(span),
+                missing_features: Some(errors::MissingFeatures),
+            })
+            .emit();
+    }
+}
+
+fn handle_lang_items(
+    tcx: TyCtxt<'_>,
+    did: LocalDefId,
+    interesting_spans: &InterestingAttributeDiagnosticSpans,
+    attrs: &[Attribute],
+    codegen_fn_attrs: &mut CodegenFnAttrs,
+) {
     // Weak lang items have the same semantics as "std internal" symbols in the
     // sense that they're preserved through all our LTO passes and only
     // strippable by the linker.
@@ -478,68 +543,59 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             codegen_fn_attrs.link_name = Some(link_name);
         }
     }
-    check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
 
+    // error when using no_mangle on a lang item item
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
         && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
     {
-        let no_mangle_span =
-            find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
-                .unwrap_or_default();
         let lang_item =
             lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
         let mut err = tcx
             .dcx()
             .struct_span_err(
-                no_mangle_span,
+                interesting_spans.no_mangle.unwrap_or_default(),
                 "`#[no_mangle]` cannot be used on internal language items",
             )
             .with_note("Rustc requires this item to have a specific mangled name.")
             .with_span_label(tcx.def_span(did), "should be the internal language item");
-        if let Some(lang_item) = lang_item {
-            if let Some(link_name) = lang_item.link_name() {
-                err = err
-                    .with_note("If you are trying to prevent mangling to ease debugging, many")
-                    .with_note(format!(
-                        "debuggers support a command such as `rbreak {link_name}` to"
-                    ))
-                    .with_note(format!(
-                        "match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
-                    ))
-            }
+        if let Some(lang_item) = lang_item
+            && let Some(link_name) = lang_item.link_name()
+        {
+            err = err
+                .with_note("If you are trying to prevent mangling to ease debugging, many")
+                .with_note(format!("debuggers support a command such as `rbreak {link_name}` to"))
+                .with_note(format!(
+                    "match `.*{link_name}.*` instead of `break {link_name}` on a specific name"
+                ))
         }
         err.emit();
     }
+}
 
-    // Any linkage to LLVM intrinsics for now forcibly marks them all as never
-    // unwinds since LLVM sometimes can't handle codegen which `invoke`s
-    // intrinsic functions.
-    if let Some(name) = &codegen_fn_attrs.link_name
-        && name.as_str().starts_with("llvm.")
-    {
-        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
+/// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]).
+///
+/// This happens in 4 stages:
+/// - apply built-in attributes that directly translate to codegen attributes.
+/// - handle lang items. These have special codegen attrs applied to them.
+/// - apply overrides, like minimum requirements for alignment and other settings that don't rely directly the built-in attrs on the item.
+///   overrides come after applying built-in attributes since they may only apply when certain attributes were already set in the stage before.
+/// - check that the result is valid. There's various ways in which this may not be the case, such as certain combinations of attrs.
+fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
+    if cfg!(debug_assertions) {
+        let def_kind = tcx.def_kind(did);
+        assert!(
+            def_kind.has_codegen_attrs(),
+            "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
+        );
     }
 
-    if let Some(features) = check_tied_features(
-        tcx.sess,
-        &codegen_fn_attrs
-            .target_features
-            .iter()
-            .map(|features| (features.name.as_str(), true))
-            .collect(),
-    ) {
-        let span =
-            find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span)
-                .unwrap_or_else(|| tcx.def_span(did));
+    let mut codegen_fn_attrs = CodegenFnAttrs::new();
+    let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
 
-        tcx.dcx()
-            .create_err(errors::TargetFeatureDisableOrEnable {
-                features,
-                span: Some(span),
-                missing_features: Some(errors::MissingFeatures),
-            })
-            .emit();
-    }
+    let interesting_spans = process_builtin_attrs(tcx, did, attrs, &mut codegen_fn_attrs);
+    handle_lang_items(tcx, did, &interesting_spans, attrs, &mut codegen_fn_attrs);
+    apply_overrides(tcx, did, &mut codegen_fn_attrs);
+    check_result(tcx, did, interesting_spans, &codegen_fn_attrs);
 
     codegen_fn_attrs
 }
@@ -566,27 +622,12 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> {
     tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment
 }
 
-fn check_link_name_xor_ordinal(
-    tcx: TyCtxt<'_>,
-    codegen_fn_attrs: &CodegenFnAttrs,
-    inline_span: Option<Span>,
-) {
-    if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
-        return;
-    }
-    let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
-    if let Some(span) = inline_span {
-        tcx.dcx().span_err(span, msg);
-    } else {
-        tcx.dcx().err(msg);
-    }
-}
-
 /// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)]
 /// macros. There are two forms. The pure one without args to mark primal functions (the functions
 /// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the
 /// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never
 /// panic, unless we introduced a bug when parsing the autodiff macro.
+//FIXME(jdonszelmann): put in the main loop. No need to have two..... :/ Let's do that when we make autodiff parsed.
 fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
     let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs
index 8e78cbe1963..f391c198e1a 100644
--- a/compiler/rustc_codegen_ssa/src/traits/write.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/write.rs
@@ -16,12 +16,6 @@ pub trait WriteBackendMethods: Clone + 'static {
     type ThinData: Send + Sync;
     type ThinBuffer: ThinBufferMethods;
 
-    /// Merge all modules into main_module and returning it
-    fn run_link(
-        cgcx: &CodegenContext<Self>,
-        dcx: DiagCtxtHandle<'_>,
-        modules: Vec<ModuleCodegen<Self::Module>>,
-    ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     /// Performs fat LTO by merging all modules into a single one, running autodiff
     /// if necessary and running any further optimizations
     fn run_and_optimize_fat_lto(