Skip to main content

rustc_codegen_llvm/
attributes.rs

1//! Set and unset common attributes on LLVM values.
2use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr, RtsanSetting};
3use rustc_hir::def_id::DefId;
4use rustc_hir::find_attr;
5use rustc_middle::middle::codegen_fn_attrs::{
6    CodegenFnAttrFlags, CodegenFnAttrs, InstrumentFnAttr, PatchableFunctionEntry, SanitizerFnAttrs,
7    TargetFeature,
8};
9use rustc_middle::ty::{self, Instance, TyCtxt};
10use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
11use rustc_span::sym;
12use rustc_symbol_mangling::mangle_internal_symbol;
13use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector};
14use smallvec::SmallVec;
15
16use crate::context::SimpleCx;
17use crate::errors::{PackedStackBackchainNeedsSoftfloat, SanitizerMemtagRequiresMte};
18use crate::llvm::AttributePlace::Function;
19use crate::llvm::{
20    self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value,
21};
22use crate::{Session, attributes, llvm_util};
23
24pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
25    if !attrs.is_empty() {
26        llvm::AddFunctionAttributes(llfn, idx, attrs);
27    }
28}
29
30pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
31    if !attrs.is_empty() {
32        llvm::AddCallSiteAttributes(callsite, idx, attrs);
33    }
34}
35
36pub(crate) fn has_string_attr(llfn: &Value, name: &str) -> bool {
37    llvm::HasStringAttribute(llfn, name)
38}
39
40pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) {
41    llvm::RemoveStringAttrFromFn(llfn, name);
42}
43
44/// Get LLVM attribute for the provided inline heuristic.
45#[inline]
46pub(crate) fn inline_attr<'tcx, 'll>(
47    cx: &SimpleCx<'ll>,
48    tcx: TyCtxt<'tcx>,
49    instance: Instance<'tcx>,
50    codegen_fn_attrs: &CodegenFnAttrs,
51) -> Option<&'ll Attribute> {
52    if !tcx.sess.opts.unstable_opts.inline_llvm {
53        // disable LLVM inlining
54        return Some(AttributeKind::NoInline.create_attr(cx.llcx));
55    }
56
57    // `optnone` requires `noinline`
58    let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
59        (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
60        (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint,
61        (inline, _) => inline,
62    };
63
64    match inline {
65        InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)),
66        InlineAttr::Always | InlineAttr::Force { .. } => {
67            Some(AttributeKind::AlwaysInline.create_attr(cx.llcx))
68        }
69        InlineAttr::Never => {
70            if tcx.sess.target.arch != Arch::AmdGpu {
71                Some(AttributeKind::NoInline.create_attr(cx.llcx))
72            } else {
73                None
74            }
75        }
76        InlineAttr::None => None,
77    }
78}
79
80#[inline]
81fn patchable_function_entry_attrs<'ll>(
82    cx: &SimpleCx<'ll>,
83    sess: &Session,
84    attr: Option<PatchableFunctionEntry>,
85) -> SmallVec<[&'ll Attribute; 2]> {
86    let mut attrs = SmallVec::new();
87    let patchable_spec = attr.unwrap_or_else(|| {
88        PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry)
89    });
90    let entry = patchable_spec.entry();
91    let prefix = patchable_spec.prefix();
92    if entry > 0 {
93        attrs.push(llvm::CreateAttrStringValue(
94            cx.llcx,
95            "patchable-function-entry",
96            &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}", entry))
    })format!("{}", entry),
97        ));
98    }
99    if prefix > 0 {
100        attrs.push(llvm::CreateAttrStringValue(
101            cx.llcx,
102            "patchable-function-prefix",
103            &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}", prefix))
    })format!("{}", prefix),
104        ));
105    }
106    attrs
107}
108
109/// Get LLVM sanitize attributes.
110#[inline]
111pub(crate) fn sanitize_attrs<'ll, 'tcx>(
112    cx: &SimpleCx<'ll>,
113    tcx: TyCtxt<'tcx>,
114    sanitizer_fn_attr: SanitizerFnAttrs,
115) -> SmallVec<[&'ll Attribute; 4]> {
116    let mut attrs = SmallVec::new();
117    let enabled = tcx.sess.sanitizers() - sanitizer_fn_attr.disabled;
118    if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
119        attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
120    }
121    if enabled.contains(SanitizerSet::MEMORY) {
122        attrs.push(llvm::AttributeKind::SanitizeMemory.create_attr(cx.llcx));
123    }
124    if enabled.contains(SanitizerSet::THREAD) {
125        attrs.push(llvm::AttributeKind::SanitizeThread.create_attr(cx.llcx));
126    }
127    if enabled.contains(SanitizerSet::HWADDRESS) || enabled.contains(SanitizerSet::KERNELHWADDRESS)
128    {
129        attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx));
130    }
131    if enabled.contains(SanitizerSet::SHADOWCALLSTACK) {
132        attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx));
133    }
134    if enabled.contains(SanitizerSet::MEMTAG) {
135        // Check to make sure the mte target feature is actually enabled.
136        let features = tcx.global_backend_features(());
137        let mte_feature =
138            features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..]));
139        if let None | Some("-mte") = mte_feature {
140            tcx.dcx().emit_err(SanitizerMemtagRequiresMte);
141        }
142
143        attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
144    }
145    if enabled.contains(SanitizerSet::SAFESTACK) {
146        attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
147    }
148    if tcx.sess.sanitizers().contains(SanitizerSet::REALTIME) {
149        match sanitizer_fn_attr.rtsan_setting {
150            RtsanSetting::Nonblocking => {
151                attrs.push(llvm::AttributeKind::SanitizeRealtimeNonblocking.create_attr(cx.llcx))
152            }
153            RtsanSetting::Blocking => {
154                attrs.push(llvm::AttributeKind::SanitizeRealtimeBlocking.create_attr(cx.llcx))
155            }
156            // caller is the default, so no llvm attribute
157            RtsanSetting::Caller => (),
158        }
159    }
160    attrs
161}
162
163/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
164#[inline]
165pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option<bool>) -> &Attribute {
166    // NOTE: We should determine if we even need async unwind tables, as they
167    // take have more overhead and if we can use sync unwind tables we
168    // probably should.
169    //
170    // Similar logic exists for the per-module uwtable annotation in `context.rs`.
171    let async_unwind = !use_sync_unwind.unwrap_or(false);
172    llvm::CreateUWTableAttr(llcx, async_unwind)
173}
174
175pub(crate) fn frame_pointer(sess: &Session) -> FramePointer {
176    let mut fp = sess.target.frame_pointer;
177    let opts = &sess.opts;
178    // "mcount" function relies on stack pointer.
179    // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>.
180    if opts.unstable_opts.instrument_mcount {
181        fp.ratchet(FramePointer::Always);
182    }
183    fp.ratchet(opts.cg.force_frame_pointers);
184    fp
185}
186
187pub(crate) fn frame_pointer_type_attr<'ll>(
188    cx: &SimpleCx<'ll>,
189    sess: &Session,
190) -> Option<&'ll Attribute> {
191    let fp = frame_pointer(sess);
192    let attr_value = match fp {
193        FramePointer::Always => "all",
194        FramePointer::NonLeaf => "non-leaf",
195        FramePointer::MayOmit => return None,
196    };
197    Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value))
198}
199
200fn function_return_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
201    let function_return_attr = match sess.opts.unstable_opts.function_return {
202        FunctionReturn::Keep => return None,
203        FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern,
204    };
205
206    Some(function_return_attr.create_attr(cx.llcx))
207}
208
209/// Tell LLVM what instrument function to insert.
210#[inline]
211fn instrument_function_attr<'ll>(
212    cx: &SimpleCx<'ll>,
213    sess: &Session,
214    instrument_fn: InstrumentFnAttr,
215) -> SmallVec<[&'ll Attribute; 4]> {
216    let mut attrs = SmallVec::new();
217    if sess.opts.unstable_opts.instrument_mcount {
218        // Similar to `clang -pg` behavior. Handled by the
219        // `post-inline-ee-instrument` LLVM pass.
220
221        let instrument_entry = match instrument_fn {
222            InstrumentFnAttr::Default | InstrumentFnAttr::On => true,
223            InstrumentFnAttr::Off => false,
224        };
225
226        if instrument_entry {
227            // The function name varies on platforms.
228            // See test/CodeGen/mcount.c in clang.
229            let mcount_name = match &sess.target.llvm_mcount_intrinsic {
230                Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(),
231                None => sess.target.mcount.as_ref(),
232            };
233
234            attrs.push(llvm::CreateAttrStringValue(
235                cx.llcx,
236                "instrument-function-entry-inlined",
237                mcount_name,
238            ));
239        }
240    }
241    if let Some(options) = &sess.opts.unstable_opts.instrument_xray {
242        // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}.
243        // Function prologue and epilogue are instrumented with NOP sleds,
244        // a runtime library later replaces them with detours into tracing code.
245
246        let mut never = options.never;
247        let mut always = options.always;
248
249        // always and never may be overridden by the #[instrument_fn = ...] attribute.
250        match instrument_fn {
251            InstrumentFnAttr::Default => {}
252            InstrumentFnAttr::On => {
253                always = true;
254            }
255            InstrumentFnAttr::Off => {
256                never = true;
257            }
258        }
259
260        if never {
261            attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never"));
262        }
263        if always {
264            attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always"));
265        }
266
267        if options.ignore_loops {
268            attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops"));
269        }
270        // LLVM will not choose the default for us, but rather requires specific
271        // threshold in absence of "xray-always". Use the same default as Clang.
272        let threshold = options.instruction_threshold.unwrap_or(200);
273        attrs.push(llvm::CreateAttrStringValue(
274            cx.llcx,
275            "xray-instruction-threshold",
276            &threshold.to_string(),
277        ));
278        if options.skip_entry {
279            attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry"));
280        }
281        if options.skip_exit {
282            attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit"));
283        }
284    }
285    attrs
286}
287
288fn nojumptables_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
289    if sess.opts.cg.jump_tables {
290        return None;
291    }
292
293    Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true"))
294}
295
296fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<&'ll Attribute> {
297    // Currently stack probes seem somewhat incompatible with the address
298    // sanitizer and thread sanitizer. With asan we're already protected from
299    // stack overflow anyway so we don't really need stack probes regardless.
300    if tcx.sess.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) {
301        return None;
302    }
303
304    // probestack doesn't play nice either with `-C profile-generate`.
305    if tcx.sess.opts.cg.profile_generate.enabled() {
306        return None;
307    }
308
309    let attr_value = match tcx.sess.target.stack_probes {
310        StackProbeType::None => return None,
311        // Request LLVM to generate the probes inline. If the given LLVM version does not support
312        // this, no probe is generated at all (even if the attribute is specified).
313        StackProbeType::Inline => "inline-asm",
314        // Flag our internal `__rust_probestack` function as the stack probe symbol.
315        // This is defined in the `compiler-builtins` crate for each architecture.
316        StackProbeType::Call => &mangle_internal_symbol(tcx, "__rust_probestack"),
317        // Pick from the two above based on the LLVM version.
318        StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
319            if llvm_util::get_version() < min_llvm_version_for_inline {
320                &mangle_internal_symbol(tcx, "__rust_probestack")
321            } else {
322                "inline-asm"
323            }
324        }
325    };
326    Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
327}
328
329fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
330    let sspattr = match sess.stack_protector() {
331        StackProtector::None => return None,
332        StackProtector::All => AttributeKind::StackProtectReq,
333        StackProtector::Strong => AttributeKind::StackProtectStrong,
334        StackProtector::Basic => AttributeKind::StackProtect,
335    };
336
337    Some(sspattr.create_attr(cx.llcx))
338}
339
340fn packed_stack_attr<'ll>(
341    cx: &SimpleCx<'ll>,
342    sess: &Session,
343    function_attributes: &Vec<TargetFeature>,
344) -> Option<&'ll Attribute> {
345    if sess.target.arch != Arch::S390x {
346        return None;
347    }
348    if !sess.opts.unstable_opts.packed_stack {
349        return None;
350    }
351
352    // The backchain and softfloat flags can be set via -Ctarget-features=...
353    // or via #[target_features(enable = ...)] so we have to check both possibilities
354    let have_backchain = sess.unstable_target_features.contains(&sym::backchain)
355        || function_attributes.iter().any(|feature| feature.name == sym::backchain);
356    let have_softfloat = sess.unstable_target_features.contains(&sym::soft_float)
357        || function_attributes.iter().any(|feature| feature.name == sym::soft_float);
358
359    // If both, backchain and packedstack, are enabled LLVM cannot generate valid function entry points
360    // with the default ABI. However if the softfloat flag is set LLVM will switch to the softfloat
361    // ABI, where this works.
362    if have_backchain && !have_softfloat {
363        sess.dcx().emit_err(PackedStackBackchainNeedsSoftfloat);
364        return None;
365    }
366
367    Some(llvm::CreateAttrString(cx.llcx, "packed-stack"))
368}
369
370pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute {
371    let target_cpu = llvm_util::target_cpu(sess);
372    llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
373}
374
375pub(crate) fn tune_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
376    llvm_util::tune_cpu(sess)
377        .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu))
378}
379
380/// Get the `target-features` LLVM attribute.
381pub(crate) fn target_features_attr<'ll, 'tcx>(
382    cx: &SimpleCx<'ll>,
383    tcx: TyCtxt<'tcx>,
384    function_features: Vec<String>,
385) -> Option<&'ll Attribute> {
386    let global_features = tcx.global_backend_features(()).iter().map(String::as_str);
387    let function_features = function_features.iter().map(String::as_str);
388    let target_features =
389        global_features.chain(function_features).intersperse(",").collect::<String>();
390    (!target_features.is_empty())
391        .then(|| llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features))
392}
393
394/// Get the `NonLazyBind` LLVM attribute,
395/// if the codegen options allow skipping the PLT.
396pub(crate) fn non_lazy_bind_attr<'ll>(
397    cx: &SimpleCx<'ll>,
398    sess: &Session,
399) -> Option<&'ll Attribute> {
400    // Don't generate calls through PLT if it's not necessary
401    if !sess.needs_plt() { Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) } else { None }
402}
403
404/// Get the default optimizations attrs for a function.
405#[inline]
406pub(crate) fn default_optimisation_attrs<'ll>(
407    cx: &SimpleCx<'ll>,
408    sess: &Session,
409) -> SmallVec<[&'ll Attribute; 2]> {
410    let mut attrs = SmallVec::new();
411    match sess.opts.optimize {
412        OptLevel::Size => {
413            attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
414        }
415        OptLevel::SizeMin => {
416            attrs.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
417            attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
418        }
419        _ => {}
420    }
421    attrs
422}
423
424fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
425    llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
426}
427
428/// Helper for `FnAbiLlvmExt::apply_attrs_llfn`:
429/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
430/// attributes.
431pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
432    cx: &SimpleCx<'ll>,
433    tcx: TyCtxt<'tcx>,
434    llfn: &'ll Value,
435    codegen_fn_attrs: &CodegenFnAttrs,
436    instance: Option<ty::Instance<'tcx>>,
437) {
438    let sess = tcx.sess;
439    let mut to_add = SmallVec::<[_; 16]>::new();
440
441    match codegen_fn_attrs.optimize {
442        OptimizeAttr::Default => {
443            to_add.extend(default_optimisation_attrs(cx, sess));
444        }
445        OptimizeAttr::DoNotOptimize => {
446            to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx));
447        }
448        OptimizeAttr::Size => {
449            to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
450            to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
451        }
452        OptimizeAttr::Speed => {}
453    }
454
455    if let Some(instance) = instance {
456        to_add.extend(inline_attr(cx, tcx, instance, codegen_fn_attrs));
457    }
458
459    if sess.must_emit_unwind_tables() {
460        to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind));
461    }
462
463    if sess.opts.unstable_opts.profile_sample_use.is_some() {
464        to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile"));
465    }
466
467    // FIXME: none of these functions interact with source level attributes.
468    to_add.extend(frame_pointer_type_attr(cx, sess));
469    to_add.extend(function_return_attr(cx, sess));
470    to_add.extend(instrument_function_attr(cx, sess, codegen_fn_attrs.instrument_fn));
471    to_add.extend(nojumptables_attr(cx, sess));
472    to_add.extend(probestack_attr(cx, tcx));
473    to_add.extend(stackprotector_attr(cx, sess));
474
475    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) {
476        to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins"));
477    }
478
479    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::OFFLOAD_KERNEL) {
480        to_add.push(llvm::CreateAttrString(cx.llcx, "offload-kernel"))
481    }
482
483    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
484        to_add.push(AttributeKind::Cold.create_attr(cx.llcx));
485    }
486    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
487        to_add.push(MemoryEffects::ReadOnly.create_attr(cx.llcx));
488    }
489    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
490        to_add.push(MemoryEffects::None.create_attr(cx.llcx));
491    }
492    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
493        // do nothing; a naked function is converted into an extern function
494        // and a global assembly block. LLVM's support for naked functions is
495        // not used.
496    } else {
497        // Do not set sanitizer attributes for naked functions.
498        to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
499
500        // For non-naked functions, set branch protection attributes on aarch64.
501        if let Some(BranchProtection { bti, pac_ret, gcs }) =
502            sess.opts.unstable_opts.branch_protection
503        {
504            if !(sess.target.arch == Arch::AArch64) {
    ::core::panicking::panic("assertion failed: sess.target.arch == Arch::AArch64")
};assert!(sess.target.arch == Arch::AArch64);
505            if bti {
506                to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
507            }
508            if gcs {
509                to_add.push(llvm::CreateAttrString(cx.llcx, "guarded-control-stack"));
510            }
511            if let Some(PacRet { leaf, pc, key }) = pac_ret {
512                if pc {
513                    to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr"));
514                }
515                to_add.push(llvm::CreateAttrStringValue(
516                    cx.llcx,
517                    "sign-return-address",
518                    if leaf { "all" } else { "non-leaf" },
519                ));
520                to_add.push(llvm::CreateAttrStringValue(
521                    cx.llcx,
522                    "sign-return-address-key",
523                    if key == PAuthKey::A { "a_key" } else { "b_key" },
524                ));
525            }
526        }
527    }
528    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
529        || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
530    {
531        to_add.push(create_alloc_family_attr(cx.llcx));
532        if let Some(instance) = instance
533            && let Some(name) =
534                {
    {
        'done:
            {
            for i in
                ::rustc_hir::attrs::HasAttrs::get_attrs(instance.def_id(),
                    &tcx) {
                #[allow(unused_imports)]
                use rustc_hir::attrs::AttributeKind::*;
                let i: &rustc_hir::Attribute = i;
                match i {
                    rustc_hir::Attribute::Parsed(RustcAllocatorZeroedVariant {
                        name }) => {
                        break 'done Some(name);
                    }
                    rustc_hir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(tcx, instance.def_id(), RustcAllocatorZeroedVariant {name} => name)
535        {
536            to_add.push(llvm::CreateAttrStringValue(
537                cx.llcx,
538                "alloc-variant-zeroed",
539                &mangle_internal_symbol(tcx, name.as_str()),
540            ));
541        }
542        // apply to argument place instead of function
543        let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
544        attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);
545        to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0));
546        let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned;
547        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
548            flags |= AllocKindFlags::Uninitialized;
549        } else {
550            flags |= AllocKindFlags::Zeroed;
551        }
552        to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags));
553        // apply to return place instead of function (unlike all other attributes applied in this
554        // function)
555        let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
556        attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
557    }
558    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) {
559        to_add.push(create_alloc_family_attr(cx.llcx));
560        to_add.push(llvm::CreateAllocKindAttr(
561            cx.llcx,
562            AllocKindFlags::Realloc | AllocKindFlags::Aligned,
563        ));
564        // applies to argument place instead of function place
565        let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
566        attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
567        // apply to argument place instead of function
568        let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
569        attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]);
570        to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3));
571        let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
572        attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
573    }
574    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) {
575        to_add.push(create_alloc_family_attr(cx.llcx));
576        to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
577        // applies to argument place instead of function place
578        let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
579        // "Does not capture provenance" means "if the function call stashes the pointer somewhere,
580        // accessing that pointer after the function returns is UB". That is definitely the case here since
581        // freeing will destroy the provenance.
582        let captures_addr = AttributeKind::CapturesAddress.create_attr(cx.llcx);
583        let attrs = &[allocated_pointer, captures_addr];
584        attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), attrs);
585    }
586    if let Some(align) = codegen_fn_attrs.alignment {
587        llvm::set_alignment(llfn, align);
588    }
589    if let Some(packed_stack) = packed_stack_attr(cx, sess, &codegen_fn_attrs.target_features) {
590        to_add.push(packed_stack);
591    }
592    to_add.extend(patchable_function_entry_attrs(
593        cx,
594        sess,
595        codegen_fn_attrs.patchable_function_entry,
596    ));
597
598    // Always annotate functions with the target-cpu they are compiled for.
599    // Without this, ThinLTO won't inline Rust functions into Clang generated
600    // functions (because Clang annotates functions this way too).
601    to_add.push(target_cpu_attr(cx, sess));
602    // tune-cpu is only conveyed through the attribute for our purpose.
603    // The target doesn't care; the subtarget reads our attribute.
604    to_add.extend(tune_cpu_attr(cx, sess));
605
606    let function_features =
607        codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
608
609    let function_features = function_features
610        .iter()
611        // Convert to LLVMFeatures and filter out unavailable ones
612        .flat_map(|feat| llvm_util::to_llvm_features(sess, feat))
613        // Convert LLVMFeatures & dependencies to +<feats>s
614        .flat_map(|feat| feat.into_iter().map(|f| ::alloc::__export::must_use({ ::alloc::fmt::format(format_args!("+{0}", f)) })format!("+{f}")))
615        .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
616            InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
617            InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),
618        }))
619        .collect::<Vec<String>>();
620
621    if sess.target.is_like_wasm {
622        // If this function is an import from the environment but the wasm
623        // import has a specific module/name, apply them here.
624        if let Some(instance) = instance
625            && let Some(module) = wasm_import_module(tcx, instance.def_id())
626        {
627            to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module));
628
629            let name =
630                codegen_fn_attrs.symbol_name.unwrap_or_else(|| tcx.item_name(instance.def_id()));
631            let name = name.as_str();
632            to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
633        }
634    }
635
636    to_add.extend(target_features_attr(cx, tcx, function_features));
637
638    attributes::apply_to_llfn(llfn, Function, &to_add);
639}
640
641fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> {
642    tcx.wasm_import_module_map(id.krate).get(&id)
643}