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