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