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