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