diff options
Diffstat (limited to 'compiler/rustc_codegen_ssa')
| -rw-r--r-- | compiler/rustc_codegen_ssa/Cargo.toml | 4 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/link.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/symbol_export.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/write.rs | 91 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/base.rs | 16 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 551 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/traits/write.rs | 6 |
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( |
