1use 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, PatchableFunctionEntry, SanitizerFnAttrs, TargetFeature,
7};
8use rustc_middle::ty::{self, Instance, TyCtxt};
9use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
10use rustc_span::sym;
11use rustc_symbol_mangling::mangle_internal_symbol;
12use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector};
13use smallvec::SmallVec;
14
15use crate::context::SimpleCx;
16use crate::errors::{PackedStackBackchainNeedsSoftfloat, SanitizerMemtagRequiresMte};
17use crate::llvm::AttributePlace::Function;
18use crate::llvm::{
19 self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value,
20};
21use crate::{Session, attributes, llvm_util};
22
23pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
24 if !attrs.is_empty() {
25 llvm::AddFunctionAttributes(llfn, idx, attrs);
26 }
27}
28
29pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
30 if !attrs.is_empty() {
31 llvm::AddCallSiteAttributes(callsite, idx, attrs);
32 }
33}
34
35pub(crate) fn has_string_attr(llfn: &Value, name: &str) -> bool {
36 llvm::HasStringAttribute(llfn, name)
37}
38
39pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) {
40 llvm::RemoveStringAttrFromFn(llfn, name);
41}
42
43#[inline]
45pub(crate) fn inline_attr<'tcx, 'll>(
46 cx: &SimpleCx<'ll>,
47 tcx: TyCtxt<'tcx>,
48 instance: Instance<'tcx>,
49 codegen_fn_attrs: &CodegenFnAttrs,
50) -> Option<&'ll Attribute> {
51 if !tcx.sess.opts.unstable_opts.inline_llvm {
52 return Some(AttributeKind::NoInline.create_attr(cx.llcx));
54 }
55
56 let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
58 (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
59 (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint,
60 (inline, _) => inline,
61 };
62
63 match inline {
64 InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)),
65 InlineAttr::Always | InlineAttr::Force { .. } => {
66 Some(AttributeKind::AlwaysInline.create_attr(cx.llcx))
67 }
68 InlineAttr::Never => {
69 if tcx.sess.target.arch != Arch::AmdGpu {
70 Some(AttributeKind::NoInline.create_attr(cx.llcx))
71 } else {
72 None
73 }
74 }
75 InlineAttr::None => None,
76 }
77}
78
79#[inline]
80fn patchable_function_entry_attrs<'ll>(
81 cx: &SimpleCx<'ll>,
82 sess: &Session,
83 attr: Option<PatchableFunctionEntry>,
84) -> SmallVec<[&'ll Attribute; 2]> {
85 let mut attrs = SmallVec::new();
86 let patchable_spec = attr.unwrap_or_else(|| {
87 PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry)
88 });
89 let entry = patchable_spec.entry();
90 let prefix = patchable_spec.prefix();
91 if entry > 0 {
92 attrs.push(llvm::CreateAttrStringValue(
93 cx.llcx,
94 "patchable-function-entry",
95 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", entry))
})format!("{}", entry),
96 ));
97 }
98 if prefix > 0 {
99 attrs.push(llvm::CreateAttrStringValue(
100 cx.llcx,
101 "patchable-function-prefix",
102 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", prefix))
})format!("{}", prefix),
103 ));
104 }
105 attrs
106}
107
108#[inline]
110pub(crate) fn sanitize_attrs<'ll, 'tcx>(
111 cx: &SimpleCx<'ll>,
112 tcx: TyCtxt<'tcx>,
113 sanitizer_fn_attr: SanitizerFnAttrs,
114) -> SmallVec<[&'ll Attribute; 4]> {
115 let mut attrs = SmallVec::new();
116 let enabled = tcx.sess.sanitizers() - sanitizer_fn_attr.disabled;
117 if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
118 attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
119 }
120 if enabled.contains(SanitizerSet::MEMORY) {
121 attrs.push(llvm::AttributeKind::SanitizeMemory.create_attr(cx.llcx));
122 }
123 if enabled.contains(SanitizerSet::THREAD) {
124 attrs.push(llvm::AttributeKind::SanitizeThread.create_attr(cx.llcx));
125 }
126 if enabled.contains(SanitizerSet::HWADDRESS) || enabled.contains(SanitizerSet::KERNELHWADDRESS)
127 {
128 attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx));
129 }
130 if enabled.contains(SanitizerSet::SHADOWCALLSTACK) {
131 attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx));
132 }
133 if enabled.contains(SanitizerSet::MEMTAG) {
134 let features = tcx.global_backend_features(());
136 let mte_feature =
137 features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..]));
138 if let None | Some("-mte") = mte_feature {
139 tcx.dcx().emit_err(SanitizerMemtagRequiresMte);
140 }
141
142 attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
143 }
144 if enabled.contains(SanitizerSet::SAFESTACK) {
145 attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
146 }
147 if tcx.sess.sanitizers().contains(SanitizerSet::REALTIME) {
148 match sanitizer_fn_attr.rtsan_setting {
149 RtsanSetting::Nonblocking => {
150 attrs.push(llvm::AttributeKind::SanitizeRealtimeNonblocking.create_attr(cx.llcx))
151 }
152 RtsanSetting::Blocking => {
153 attrs.push(llvm::AttributeKind::SanitizeRealtimeBlocking.create_attr(cx.llcx))
154 }
155 RtsanSetting::Caller => (),
157 }
158 }
159 attrs
160}
161
162#[inline]
164pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option<bool>) -> &Attribute {
165 let async_unwind = !use_sync_unwind.unwrap_or(false);
171 llvm::CreateUWTableAttr(llcx, async_unwind)
172}
173
174pub(crate) fn frame_pointer(sess: &Session) -> FramePointer {
175 let mut fp = sess.target.frame_pointer;
176 let opts = &sess.opts;
177 if opts.unstable_opts.instrument_mcount {
180 fp.ratchet(FramePointer::Always);
181 }
182 fp.ratchet(opts.cg.force_frame_pointers);
183 fp
184}
185
186pub(crate) fn frame_pointer_type_attr<'ll>(
187 cx: &SimpleCx<'ll>,
188 sess: &Session,
189) -> Option<&'ll Attribute> {
190 let fp = frame_pointer(sess);
191 let attr_value = match fp {
192 FramePointer::Always => "all",
193 FramePointer::NonLeaf => "non-leaf",
194 FramePointer::MayOmit => return None,
195 };
196 Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value))
197}
198
199fn function_return_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
200 let function_return_attr = match sess.opts.unstable_opts.function_return {
201 FunctionReturn::Keep => return None,
202 FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern,
203 };
204
205 Some(function_return_attr.create_attr(cx.llcx))
206}
207
208#[inline]
210fn instrument_function_attr<'ll>(
211 cx: &SimpleCx<'ll>,
212 sess: &Session,
213) -> SmallVec<[&'ll Attribute; 4]> {
214 let mut attrs = SmallVec::new();
215 if sess.opts.unstable_opts.instrument_mcount {
216 let mcount_name = match &sess.target.llvm_mcount_intrinsic {
222 Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(),
223 None => sess.target.mcount.as_ref(),
224 };
225
226 attrs.push(llvm::CreateAttrStringValue(
227 cx.llcx,
228 "instrument-function-entry-inlined",
229 mcount_name,
230 ));
231 }
232 if let Some(options) = &sess.opts.unstable_opts.instrument_xray {
233 if options.always {
237 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always"));
238 }
239 if options.never {
240 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never"));
241 }
242 if options.ignore_loops {
243 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops"));
244 }
245 let threshold = options.instruction_threshold.unwrap_or(200);
248 attrs.push(llvm::CreateAttrStringValue(
249 cx.llcx,
250 "xray-instruction-threshold",
251 &threshold.to_string(),
252 ));
253 if options.skip_entry {
254 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry"));
255 }
256 if options.skip_exit {
257 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit"));
258 }
259 }
260 attrs
261}
262
263fn nojumptables_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
264 if sess.opts.cg.jump_tables {
265 return None;
266 }
267
268 Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true"))
269}
270
271fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<&'ll Attribute> {
272 if tcx.sess.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) {
276 return None;
277 }
278
279 if tcx.sess.opts.cg.profile_generate.enabled() {
281 return None;
282 }
283
284 let attr_value = match tcx.sess.target.stack_probes {
285 StackProbeType::None => return None,
286 StackProbeType::Inline => "inline-asm",
289 StackProbeType::Call => &mangle_internal_symbol(tcx, "__rust_probestack"),
292 StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
294 if llvm_util::get_version() < min_llvm_version_for_inline {
295 &mangle_internal_symbol(tcx, "__rust_probestack")
296 } else {
297 "inline-asm"
298 }
299 }
300 };
301 Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
302}
303
304fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
305 let sspattr = match sess.stack_protector() {
306 StackProtector::None => return None,
307 StackProtector::All => AttributeKind::StackProtectReq,
308 StackProtector::Strong => AttributeKind::StackProtectStrong,
309 StackProtector::Basic => AttributeKind::StackProtect,
310 };
311
312 Some(sspattr.create_attr(cx.llcx))
313}
314
315fn packed_stack_attr<'ll>(
316 cx: &SimpleCx<'ll>,
317 sess: &Session,
318 function_attributes: &Vec<TargetFeature>,
319) -> Option<&'ll Attribute> {
320 if sess.target.arch != Arch::S390x {
321 return None;
322 }
323 if !sess.opts.unstable_opts.packed_stack {
324 return None;
325 }
326
327 let have_backchain = sess.unstable_target_features.contains(&sym::backchain)
330 || function_attributes.iter().any(|feature| feature.name == sym::backchain);
331 let have_softfloat = sess.unstable_target_features.contains(&sym::soft_float)
332 || function_attributes.iter().any(|feature| feature.name == sym::soft_float);
333
334 if have_backchain && !have_softfloat {
338 sess.dcx().emit_err(PackedStackBackchainNeedsSoftfloat);
339 return None;
340 }
341
342 Some(llvm::CreateAttrString(cx.llcx, "packed-stack"))
343}
344
345pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute {
346 let target_cpu = llvm_util::target_cpu(sess);
347 llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
348}
349
350pub(crate) fn tune_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
351 llvm_util::tune_cpu(sess)
352 .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu))
353}
354
355pub(crate) fn target_features_attr<'ll, 'tcx>(
357 cx: &SimpleCx<'ll>,
358 tcx: TyCtxt<'tcx>,
359 function_features: Vec<String>,
360) -> Option<&'ll Attribute> {
361 let global_features = tcx.global_backend_features(()).iter().map(String::as_str);
362 let function_features = function_features.iter().map(String::as_str);
363 let target_features =
364 global_features.chain(function_features).intersperse(",").collect::<String>();
365 (!target_features.is_empty())
366 .then(|| llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features))
367}
368
369pub(crate) fn non_lazy_bind_attr<'ll>(
372 cx: &SimpleCx<'ll>,
373 sess: &Session,
374) -> Option<&'ll Attribute> {
375 if !sess.needs_plt() { Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) } else { None }
377}
378
379#[inline]
381pub(crate) fn default_optimisation_attrs<'ll>(
382 cx: &SimpleCx<'ll>,
383 sess: &Session,
384) -> SmallVec<[&'ll Attribute; 2]> {
385 let mut attrs = SmallVec::new();
386 match sess.opts.optimize {
387 OptLevel::Size => {
388 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
389 }
390 OptLevel::SizeMin => {
391 attrs.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
392 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
393 }
394 _ => {}
395 }
396 attrs
397}
398
399fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
400 llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
401}
402
403pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
407 cx: &SimpleCx<'ll>,
408 tcx: TyCtxt<'tcx>,
409 llfn: &'ll Value,
410 codegen_fn_attrs: &CodegenFnAttrs,
411 instance: Option<ty::Instance<'tcx>>,
412) {
413 let sess = tcx.sess;
414 let mut to_add = SmallVec::<[_; 16]>::new();
415
416 match codegen_fn_attrs.optimize {
417 OptimizeAttr::Default => {
418 to_add.extend(default_optimisation_attrs(cx, sess));
419 }
420 OptimizeAttr::DoNotOptimize => {
421 to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx));
422 }
423 OptimizeAttr::Size => {
424 to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
425 to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
426 }
427 OptimizeAttr::Speed => {}
428 }
429
430 if let Some(instance) = instance {
431 to_add.extend(inline_attr(cx, tcx, instance, codegen_fn_attrs));
432 }
433
434 if sess.must_emit_unwind_tables() {
435 to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind));
436 }
437
438 if sess.opts.unstable_opts.profile_sample_use.is_some() {
439 to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile"));
440 }
441
442 to_add.extend(frame_pointer_type_attr(cx, sess));
444 to_add.extend(function_return_attr(cx, sess));
445 to_add.extend(instrument_function_attr(cx, sess));
446 to_add.extend(nojumptables_attr(cx, sess));
447 to_add.extend(probestack_attr(cx, tcx));
448 to_add.extend(stackprotector_attr(cx, sess));
449
450 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) {
451 to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins"));
452 }
453
454 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::OFFLOAD_KERNEL) {
455 to_add.push(llvm::CreateAttrString(cx.llcx, "offload-kernel"))
456 }
457
458 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
459 to_add.push(AttributeKind::Cold.create_attr(cx.llcx));
460 }
461 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
462 to_add.push(MemoryEffects::ReadOnly.create_attr(cx.llcx));
463 }
464 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
465 to_add.push(MemoryEffects::None.create_attr(cx.llcx));
466 }
467 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
468 } else {
472 to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
474
475 if let Some(BranchProtection { bti, pac_ret, gcs }) =
477 sess.opts.unstable_opts.branch_protection
478 {
479 if !(sess.target.arch == Arch::AArch64) {
::core::panicking::panic("assertion failed: sess.target.arch == Arch::AArch64")
};assert!(sess.target.arch == Arch::AArch64);
480 if bti {
481 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
482 }
483 if gcs {
484 to_add.push(llvm::CreateAttrString(cx.llcx, "guarded-control-stack"));
485 }
486 if let Some(PacRet { leaf, pc, key }) = pac_ret {
487 if pc {
488 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr"));
489 }
490 to_add.push(llvm::CreateAttrStringValue(
491 cx.llcx,
492 "sign-return-address",
493 if leaf { "all" } else { "non-leaf" },
494 ));
495 to_add.push(llvm::CreateAttrStringValue(
496 cx.llcx,
497 "sign-return-address-key",
498 if key == PAuthKey::A { "a_key" } else { "b_key" },
499 ));
500 }
501 }
502 }
503 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
504 || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
505 {
506 to_add.push(create_alloc_family_attr(cx.llcx));
507 if let Some(instance) = instance
508 && let Some(name) =
509 {
{
'done:
{
for i in
::rustc_hir::attrs::HasAttrs::get_attrs(instance.def_id(),
&tcx) {
#[allow(unused_imports)]
use rustc_hir::attrs::AttributeKind::*;
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(RustcAllocatorZeroedVariant {
name }) => {
break 'done Some(name);
}
rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}find_attr!(tcx, instance.def_id(), RustcAllocatorZeroedVariant {name} => name)
510 {
511 to_add.push(llvm::CreateAttrStringValue(
512 cx.llcx,
513 "alloc-variant-zeroed",
514 &mangle_internal_symbol(tcx, name.as_str()),
515 ));
516 }
517 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
519 attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);
520 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0));
521 let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned;
522 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
523 flags |= AllocKindFlags::Uninitialized;
524 } else {
525 flags |= AllocKindFlags::Zeroed;
526 }
527 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags));
528 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
531 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
532 }
533 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) {
534 to_add.push(create_alloc_family_attr(cx.llcx));
535 to_add.push(llvm::CreateAllocKindAttr(
536 cx.llcx,
537 AllocKindFlags::Realloc | AllocKindFlags::Aligned,
538 ));
539 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
541 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
542 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
544 attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]);
545 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3));
546 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
547 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
548 }
549 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) {
550 to_add.push(create_alloc_family_attr(cx.llcx));
551 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
552 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
554 let captures_addr = AttributeKind::CapturesAddress.create_attr(cx.llcx);
558 let attrs = &[allocated_pointer, captures_addr];
559 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), attrs);
560 }
561 if let Some(align) = codegen_fn_attrs.alignment {
562 llvm::set_alignment(llfn, align);
563 }
564 if let Some(packed_stack) = packed_stack_attr(cx, sess, &codegen_fn_attrs.target_features) {
565 to_add.push(packed_stack);
566 }
567 to_add.extend(patchable_function_entry_attrs(
568 cx,
569 sess,
570 codegen_fn_attrs.patchable_function_entry,
571 ));
572
573 to_add.push(target_cpu_attr(cx, sess));
577 to_add.extend(tune_cpu_attr(cx, sess));
580
581 let function_features =
582 codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
583
584 let function_features = function_features
585 .iter()
586 .flat_map(|feat| llvm_util::to_llvm_features(sess, feat))
588 .flat_map(|feat| feat.into_iter().map(|f| ::alloc::__export::must_use({ ::alloc::fmt::format(format_args!("+{0}", f)) })format!("+{f}")))
590 .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
591 InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
592 InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),
593 }))
594 .collect::<Vec<String>>();
595
596 if sess.target.is_like_wasm {
597 if let Some(instance) = instance
600 && let Some(module) = wasm_import_module(tcx, instance.def_id())
601 {
602 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module));
603
604 let name =
605 codegen_fn_attrs.symbol_name.unwrap_or_else(|| tcx.item_name(instance.def_id()));
606 let name = name.as_str();
607 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
608 }
609 }
610
611 to_add.extend(target_features_attr(cx, tcx, function_features));
612
613 attributes::apply_to_llfn(llfn, Function, &to_add);
614}
615
616fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> {
617 tcx.wasm_import_module_map(id.krate).get(&id)
618}