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