clean up codegen fn attrs
This commit is contained in:
@@ -4,12 +4,12 @@ use rustc_abi::{Align, ExternAbi};
|
|||||||
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
|
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
|
||||||
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
|
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
|
||||||
use rustc_attr_data_structures::{
|
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::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||||
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
|
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::{
|
use rustc_middle::middle::codegen_fn_attrs::{
|
||||||
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
|
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 {
|
/// In some cases, attributes are only valid on functions, but it's the `check_attr`
|
||||||
if cfg!(debug_assertions) {
|
/// pass that checks that they aren't used anywhere else, rather than this module.
|
||||||
let def_kind = tcx.def_kind(did);
|
/// In these cases, we bail from performing further checks that are only meaningful for
|
||||||
assert!(
|
/// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
|
||||||
def_kind.has_codegen_attrs(),
|
/// report a delayed bug, just in case `check_attr` isn't doing its job.
|
||||||
"unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
|
Some(sanitizer_set)
|
||||||
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 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
|
// If our rustc version supports autodiff/enzyme, then we call our handler
|
||||||
// to check for any `#[rustc_autodiff(...)]` attributes.
|
// to check for any `#[rustc_autodiff(...)]` attributes.
|
||||||
|
// FIXME(jdonszelmann): merge with loop below
|
||||||
if cfg!(llvm_enzyme) {
|
if cfg!(llvm_enzyme) {
|
||||||
let ad = autodiff_attrs(tcx, did.into());
|
let ad = autodiff_attrs(tcx, did.into());
|
||||||
codegen_fn_attrs.autodiff_item = ad;
|
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() {
|
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 {
|
if let hir::Attribute::Parsed(p) = attr {
|
||||||
match p {
|
match p {
|
||||||
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
|
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
|
||||||
AttributeKind::ExportName { name, .. } => {
|
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::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
|
||||||
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
|
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
|
||||||
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
|
AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name),
|
||||||
AttributeKind::LinkOrdinal { ordinal, span } => {
|
AttributeKind::LinkOrdinal { ordinal, span } => {
|
||||||
codegen_fn_attrs.link_ordinal = Some(*ordinal);
|
codegen_fn_attrs.link_ordinal = Some(*ordinal);
|
||||||
link_ordinal_span = Some(*span);
|
interesting_spans.link_ordinal = Some(*span);
|
||||||
}
|
}
|
||||||
AttributeKind::LinkSection { name, .. } => {
|
AttributeKind::LinkSection { name, .. } => {
|
||||||
codegen_fn_attrs.link_section = Some(*name)
|
codegen_fn_attrs.link_section = Some(*name)
|
||||||
}
|
}
|
||||||
AttributeKind::NoMangle(attr_span) => {
|
AttributeKind::NoMangle(attr_span) => {
|
||||||
|
interesting_spans.no_mangle = Some(*attr_span);
|
||||||
if tcx.opt_item_name(did.to_def_id()).is_some() {
|
if tcx.opt_item_name(did.to_def_id()).is_some() {
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||||
} else {
|
} 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) => {
|
AttributeKind::TargetFeature(features, attr_span) => {
|
||||||
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
|
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");
|
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());
|
let is_closure = tcx.is_closure_like(did.to_def_id());
|
||||||
|
|
||||||
if !is_closure
|
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
|
&& fn_sig.skip_binder().abi() != ExternAbi::Rust
|
||||||
{
|
{
|
||||||
tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
|
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::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
|
||||||
sym::linkage => {
|
sym::linkage => {
|
||||||
if let Some(val) = attr.value_str() {
|
let linkage = parse_linkage_attr(tcx, did, attr);
|
||||||
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()) {
|
if tcx.is_foreign_item(did) {
|
||||||
let mut diag = tcx.dcx().struct_span_err(
|
codegen_fn_attrs.import_linkage = linkage;
|
||||||
attr.span(),
|
|
||||||
"extern mutable statics are not allowed with `#[linkage]`",
|
if tcx.is_mutable_static(did.into()) {
|
||||||
);
|
let mut diag = tcx.dcx().struct_span_err(
|
||||||
diag.note(
|
attr.span(),
|
||||||
"marking the extern static mutable would allow changing which \
|
"extern mutable statics are not allowed with `#[linkage]`",
|
||||||
symbol the static references rather than make the target of the \
|
);
|
||||||
symbol mutable",
|
diag.note(
|
||||||
);
|
"marking the extern static mutable would allow changing which \
|
||||||
diag.emit();
|
symbol the static references rather than make the target of the \
|
||||||
}
|
symbol mutable",
|
||||||
} else {
|
);
|
||||||
codegen_fn_attrs.linkage = linkage;
|
diag.emit();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
codegen_fn_attrs.linkage = linkage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sym::no_sanitize => {
|
sym::no_sanitize => {
|
||||||
no_sanitize_span = Some(attr.span());
|
interesting_spans.no_sanitize = Some(attr.span());
|
||||||
if let Some(list) = attr.meta_item_list() {
|
codegen_fn_attrs.no_sanitize |=
|
||||||
for item in list.iter() {
|
parse_no_sanitize_attr(tcx, attr).unwrap_or_default();
|
||||||
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() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sym::instruction_set => {
|
sym::instruction_set => {
|
||||||
codegen_fn_attrs.instruction_set =
|
codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr)
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
sym::patchable_function_entry => {
|
sym::patchable_function_entry => {
|
||||||
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
|
codegen_fn_attrs.patchable_function_entry =
|
||||||
let mut prefix = None;
|
parse_patchable_function_entry(tcx, attr);
|
||||||
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),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// 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
|
// 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.
|
// 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.
|
// 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));
|
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,
|
// 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
|
// 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.
|
// 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.inline = InlineAttr::Never;
|
||||||
}
|
}
|
||||||
|
|
||||||
codegen_fn_attrs.optimize =
|
|
||||||
find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
|
|
||||||
|
|
||||||
// #73631: closures inherit `#[target_feature]` annotations
|
// #73631: closures inherit `#[target_feature]` annotations
|
||||||
//
|
//
|
||||||
// If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`.
|
// 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
|
// If a function uses `#[target_feature]` it can't be inlined into general
|
||||||
// purpose functions as they wouldn't have the right target features
|
// 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
|
// 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).
|
// llvm/llvm-project#70563).
|
||||||
if !codegen_fn_attrs.target_features.is_empty()
|
if !codegen_fn_attrs.target_features.is_empty()
|
||||||
&& matches!(codegen_fn_attrs.inline, InlineAttr::Always)
|
&& 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]`");
|
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()
|
if !codegen_fn_attrs.no_sanitize.is_empty()
|
||||||
&& codegen_fn_attrs.inline.always()
|
&& 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);
|
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| {
|
tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| {
|
||||||
@@ -462,51 +486,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weak lang items have the same semantics as "std internal" symbols in the
|
// error when specifying link_name together with link_ordinal
|
||||||
// sense that they're preserved through all our LTO passes and only
|
if let Some(_) = codegen_fn_attrs.link_name
|
||||||
// strippable by the linker.
|
&& let Some(_) = codegen_fn_attrs.link_ordinal
|
||||||
//
|
|
||||||
// Additionally weak lang items have predetermined symbol names.
|
|
||||||
if let Some((name, _)) = lang_items::extract(attrs)
|
|
||||||
&& let Some(lang_item) = LangItem::from_name(name)
|
|
||||||
{
|
{
|
||||||
if WEAK_LANG_ITEMS.contains(&lang_item) {
|
let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
|
||||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
|
if let Some(span) = interesting_spans.link_ordinal {
|
||||||
|
tcx.dcx().span_err(span, msg);
|
||||||
|
} else {
|
||||||
|
tcx.dcx().err(msg);
|
||||||
}
|
}
|
||||||
if let Some(link_name) = lang_item.link_name() {
|
|
||||||
codegen_fn_attrs.export_name = Some(link_name);
|
|
||||||
codegen_fn_attrs.link_name = Some(link_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
|
|
||||||
|
|
||||||
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,
|
|
||||||
"`#[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
|
|
||||||
&& 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(features) = check_tied_features(
|
if let Some(features) = check_tied_features(
|
||||||
@@ -529,6 +518,84 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||||||
})
|
})
|
||||||
.emit();
|
.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.
|
||||||
|
//
|
||||||
|
// Additionally weak lang items have predetermined symbol names.
|
||||||
|
if let Some((name, _)) = lang_items::extract(attrs)
|
||||||
|
&& let Some(lang_item) = LangItem::from_name(name)
|
||||||
|
{
|
||||||
|
if WEAK_LANG_ITEMS.contains(&lang_item) {
|
||||||
|
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
|
||||||
|
}
|
||||||
|
if let Some(link_name) = lang_item.link_name() {
|
||||||
|
codegen_fn_attrs.export_name = Some(link_name);
|
||||||
|
codegen_fn_attrs.link_name = Some(link_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 lang_item =
|
||||||
|
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
|
||||||
|
let mut err = tcx
|
||||||
|
.dcx()
|
||||||
|
.struct_span_err(
|
||||||
|
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
|
||||||
|
&& 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut codegen_fn_attrs = CodegenFnAttrs::new();
|
||||||
|
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did));
|
||||||
|
|
||||||
|
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
|
codegen_fn_attrs
|
||||||
}
|
}
|
||||||
@@ -555,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
|
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(...)]
|
/// 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
|
/// 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
|
/// 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
|
/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never
|
||||||
/// panic, unless we introduced a bug when parsing the autodiff macro.
|
/// 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> {
|
fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> {
|
||||||
let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
|
let attrs = tcx.get_attrs(id, sym::rustc_autodiff);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user