rustc_codegen_llvm/
attributes.rs

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