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