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