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