rustc_middle/middle/
codegen_fn_attrs.rs

1use std::borrow::Cow;
2
3use rustc_abi::Align;
4use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, Linkage, OptimizeAttr};
5use rustc_macros::{HashStable, TyDecodable, TyEncodable};
6use rustc_span::Symbol;
7use rustc_target::spec::SanitizerSet;
8
9use crate::ty::{InstanceKind, TyCtxt};
10
11impl<'tcx> TyCtxt<'tcx> {
12    pub fn codegen_instance_attrs(
13        self,
14        instance_kind: InstanceKind<'_>,
15    ) -> Cow<'tcx, CodegenFnAttrs> {
16        // NOTE: we try to not clone the `CodegenFnAttrs` when that is not needed.
17        // The `to_mut` method used below clones the inner value.
18        let mut attrs = Cow::Borrowed(self.codegen_fn_attrs(instance_kind.def_id()));
19
20        // Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that
21        // are generated for indirect function calls.
22        if !matches!(instance_kind, InstanceKind::Item(_)) {
23            if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
24                attrs.to_mut().flags.remove(CodegenFnAttrFlags::NAKED);
25            }
26        }
27
28        // A shim created by `#[track_caller]` should not inherit any attributes
29        // that modify the symbol name. Failing to remove these attributes from
30        // the shim leads to errors like `symbol `foo` is already defined`.
31        //
32        // A `ClosureOnceShim` with the track_caller attribute does not have a symbol,
33        // and therefore can be skipped here.
34        if let InstanceKind::ReifyShim(_, _) = instance_kind
35            && attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
36        {
37            if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
38                attrs.to_mut().flags.remove(CodegenFnAttrFlags::NO_MANGLE);
39            }
40
41            if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
42                attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
43            }
44
45            if attrs.symbol_name.is_some() {
46                attrs.to_mut().symbol_name = None;
47            }
48        }
49
50        attrs
51    }
52}
53
54#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
55pub struct CodegenFnAttrs {
56    pub flags: CodegenFnAttrFlags,
57    /// Parsed representation of the `#[inline]` attribute
58    pub inline: InlineAttr,
59    /// Parsed representation of the `#[optimize]` attribute
60    pub optimize: OptimizeAttr,
61    /// The name this function will be imported/exported under. This can be set
62    /// using the `#[export_name = "..."]` or `#[link_name = "..."]` attribute
63    /// depending on if this is a function definition or foreign function.
64    pub symbol_name: Option<Symbol>,
65    /// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an
66    /// imported function has in the dynamic library. Note that this must not
67    /// be set when `link_name` is set. This is for foreign items with the
68    /// "raw-dylib" kind.
69    pub link_ordinal: Option<u16>,
70    /// The `#[target_feature(enable = "...")]` attribute and the enabled
71    /// features (only enabled features are supported right now).
72    /// Implied target features have already been applied.
73    pub target_features: Vec<TargetFeature>,
74    /// Whether the function was declared safe, but has target features
75    pub safe_target_features: bool,
76    /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
77    pub linkage: Option<Linkage>,
78    /// The `#[linkage = "..."]` attribute on foreign items and the value we found.
79    pub import_linkage: Option<Linkage>,
80    /// The `#[link_section = "..."]` attribute, or what executable section this
81    /// should be placed in.
82    pub link_section: Option<Symbol>,
83    /// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which
84    /// instrumentation should be disabled inside the function.
85    pub no_sanitize: SanitizerSet,
86    /// The `#[instruction_set(set)]` attribute. Indicates if the generated code should
87    /// be generated against a specific instruction set. Only usable on architectures which allow
88    /// switching between multiple instruction sets.
89    pub instruction_set: Option<InstructionSetAttr>,
90    /// The `#[align(...)]` attribute. Determines the alignment of the function body.
91    // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
92    pub alignment: Option<Align>,
93    /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
94    /// the function entry.
95    pub patchable_function_entry: Option<PatchableFunctionEntry>,
96    /// The `#[rustc_objc_class = "..."]` attribute.
97    pub objc_class: Option<Symbol>,
98    /// The `#[rustc_objc_selector = "..."]` attribute.
99    pub objc_selector: Option<Symbol>,
100}
101
102#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)]
103pub enum TargetFeatureKind {
104    /// The feature is implied by another feature, rather than explicitly added by the
105    /// `#[target_feature]` attribute
106    Implied,
107    /// The feature is added by the regular `target_feature` attribute.
108    Enabled,
109    /// The feature is added by the unsafe `force_target_feature` attribute.
110    Forced,
111}
112
113#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, HashStable)]
114pub struct TargetFeature {
115    /// The name of the target feature (e.g. "avx")
116    pub name: Symbol,
117    /// The way this feature was enabled.
118    pub kind: TargetFeatureKind,
119}
120
121#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
122pub struct PatchableFunctionEntry {
123    /// Nops to prepend to the function
124    prefix: u8,
125    /// Nops after entry, but before body
126    entry: u8,
127}
128
129impl PatchableFunctionEntry {
130    pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self {
131        Self { prefix: config.prefix(), entry: config.entry() }
132    }
133    pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self {
134        Self { prefix, entry }
135    }
136    pub fn prefix(&self) -> u8 {
137        self.prefix
138    }
139    pub fn entry(&self) -> u8 {
140        self.entry
141    }
142}
143
144#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
145pub struct CodegenFnAttrFlags(u32);
146bitflags::bitflags! {
147    impl CodegenFnAttrFlags: u32 {
148        /// `#[cold]`: a hint to LLVM that this function, when called, is never on
149        /// the hot path.
150        const COLD                      = 1 << 0;
151        /// `#[rustc_nounwind]`: An indicator that function will never unwind.
152        const NEVER_UNWIND              = 1 << 1;
153        /// `#[naked]`: an indicator to LLVM that no function prologue/epilogue
154        /// should be generated.
155        const NAKED                     = 1 << 2;
156        /// `#[no_mangle]`: an indicator that the function's name should be the same
157        /// as its symbol.
158        const NO_MANGLE                 = 1 << 3;
159        /// `#[rustc_std_internal_symbol]`: an indicator that this symbol is a
160        /// "weird symbol" for the standard library in that it has slightly
161        /// different linkage, visibility, and reachability rules.
162        const RUSTC_STD_INTERNAL_SYMBOL = 1 << 4;
163        /// `#[thread_local]`: indicates a static is actually a thread local
164        /// piece of memory
165        const THREAD_LOCAL              = 1 << 5;
166        /// `#[used(compiler)]`: indicates that LLVM can't eliminate this function (but the
167        /// linker can!).
168        const USED_COMPILER             = 1 << 6;
169        /// `#[used(linker)]`:
170        /// indicates that neither LLVM nor the linker will eliminate this function.
171        const USED_LINKER               = 1 << 7;
172        /// `#[track_caller]`: allow access to the caller location
173        const TRACK_CALLER              = 1 << 8;
174        /// #[ffi_pure]: applies clang's `pure` attribute to a foreign function
175        /// declaration.
176        const FFI_PURE                  = 1 << 9;
177        /// #[ffi_const]: applies clang's `const` attribute to a foreign function
178        /// declaration.
179        const FFI_CONST                 = 1 << 10;
180        /// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this
181        /// function is never null and the function has no side effects other than allocating.
182        const ALLOCATOR                 = 1 << 11;
183        /// `#[rustc_deallocator]`: a hint to LLVM that the function only deallocates memory.
184        const DEALLOCATOR               = 1 << 12;
185        /// `#[rustc_reallocator]`: a hint to LLVM that the function only reallocates memory.
186        const REALLOCATOR               = 1 << 13;
187        /// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory.
188        const ALLOCATOR_ZEROED          = 1 << 14;
189        /// `#[no_builtins]`: indicates that disable implicit builtin knowledge of functions for the function.
190        const NO_BUILTINS               = 1 << 15;
191        /// Marks foreign items, to make `contains_extern_indicator` cheaper.
192        const FOREIGN_ITEM              = 1 << 16;
193    }
194}
195rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags }
196
197impl CodegenFnAttrs {
198    pub const EMPTY: &'static Self = &Self::new();
199
200    pub const fn new() -> CodegenFnAttrs {
201        CodegenFnAttrs {
202            flags: CodegenFnAttrFlags::empty(),
203            inline: InlineAttr::None,
204            optimize: OptimizeAttr::Default,
205            symbol_name: None,
206            link_ordinal: None,
207            target_features: vec![],
208            safe_target_features: false,
209            linkage: None,
210            import_linkage: None,
211            link_section: None,
212            no_sanitize: SanitizerSet::empty(),
213            instruction_set: None,
214            alignment: None,
215            patchable_function_entry: None,
216            objc_class: None,
217            objc_selector: None,
218        }
219    }
220
221    /// Returns `true` if it looks like this symbol needs to be exported, for example:
222    ///
223    /// * `#[no_mangle]` is present
224    /// * `#[export_name(...)]` is present
225    /// * `#[linkage]` is present
226    ///
227    /// Keep this in sync with the logic for the unused_attributes for `#[inline]` lint.
228    pub fn contains_extern_indicator(&self) -> bool {
229        if self.flags.contains(CodegenFnAttrFlags::FOREIGN_ITEM) {
230            return false;
231        }
232
233        self.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
234            || self.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
235            || self.symbol_name.is_some()
236            || match self.linkage {
237                // These are private, so make sure we don't try to consider
238                // them external.
239                None | Some(Linkage::Internal) => false,
240                Some(_) => true,
241            }
242    }
243}