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