Skip to main content

rustc_codegen_llvm/
attributes.rs

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