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