1use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr, RtsanSetting};
3use rustc_hir::def_id::DefId;
4use rustc_middle::middle::codegen_fn_attrs::{
5 CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs,
6};
7use rustc_middle::ty::{self, TyCtxt};
8use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
9use rustc_symbol_mangling::mangle_internal_symbol;
10use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector};
11use smallvec::SmallVec;
12
13use crate::context::SimpleCx;
14use crate::errors::SanitizerMemtagRequiresMte;
15use crate::llvm::AttributePlace::Function;
16use crate::llvm::{
17 self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects, Value,
18};
19use crate::{Session, attributes, llvm_util};
20
21pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
22 if !attrs.is_empty() {
23 llvm::AddFunctionAttributes(llfn, idx, attrs);
24 }
25}
26
27pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
28 if !attrs.is_empty() {
29 llvm::AddCallSiteAttributes(callsite, idx, attrs);
30 }
31}
32
33pub(crate) fn inline_attr<'ll, 'tcx>(
35 cx: &SimpleCx<'ll>,
36 tcx: TyCtxt<'tcx>,
37 instance: ty::Instance<'tcx>,
38) -> Option<&'ll Attribute> {
39 let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id());
41 let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
42 (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
43 (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint,
44 (inline, _) => inline,
45 };
46
47 if !tcx.sess.opts.unstable_opts.inline_llvm {
48 return Some(AttributeKind::NoInline.create_attr(cx.llcx));
50 }
51 match inline {
52 InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)),
53 InlineAttr::Always | InlineAttr::Force { .. } => {
54 Some(AttributeKind::AlwaysInline.create_attr(cx.llcx))
55 }
56 InlineAttr::Never => {
57 if tcx.sess.target.arch != Arch::AmdGpu {
58 Some(AttributeKind::NoInline.create_attr(cx.llcx))
59 } else {
60 None
61 }
62 }
63 InlineAttr::None => None,
64 }
65}
66
67#[inline]
68fn patchable_function_entry_attrs<'ll>(
69 cx: &SimpleCx<'ll>,
70 sess: &Session,
71 attr: Option<PatchableFunctionEntry>,
72) -> SmallVec<[&'ll Attribute; 2]> {
73 let mut attrs = SmallVec::new();
74 let patchable_spec = attr.unwrap_or_else(|| {
75 PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry)
76 });
77 let entry = patchable_spec.entry();
78 let prefix = patchable_spec.prefix();
79 if entry > 0 {
80 attrs.push(llvm::CreateAttrStringValue(
81 cx.llcx,
82 "patchable-function-entry",
83 &format!("{}", entry),
84 ));
85 }
86 if prefix > 0 {
87 attrs.push(llvm::CreateAttrStringValue(
88 cx.llcx,
89 "patchable-function-prefix",
90 &format!("{}", prefix),
91 ));
92 }
93 attrs
94}
95
96#[inline]
98pub(crate) fn sanitize_attrs<'ll, 'tcx>(
99 cx: &SimpleCx<'ll>,
100 tcx: TyCtxt<'tcx>,
101 sanitizer_fn_attr: SanitizerFnAttrs,
102) -> SmallVec<[&'ll Attribute; 4]> {
103 let mut attrs = SmallVec::new();
104 let enabled = tcx.sess.sanitizers() - sanitizer_fn_attr.disabled;
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 let features = 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 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 if tcx.sess.sanitizers().contains(SanitizerSet::REALTIME) {
135 match sanitizer_fn_attr.rtsan_setting {
136 RtsanSetting::Nonblocking => {
137 attrs.push(llvm::AttributeKind::SanitizeRealtimeNonblocking.create_attr(cx.llcx))
138 }
139 RtsanSetting::Blocking => {
140 attrs.push(llvm::AttributeKind::SanitizeRealtimeBlocking.create_attr(cx.llcx))
141 }
142 RtsanSetting::Caller => (),
144 }
145 }
146 attrs
147}
148
149#[inline]
151pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option<bool>) -> &Attribute {
152 let async_unwind = !use_sync_unwind.unwrap_or(false);
156 llvm::CreateUWTableAttr(llcx, async_unwind)
157}
158
159pub(crate) fn frame_pointer_type_attr<'ll>(
160 cx: &SimpleCx<'ll>,
161 sess: &Session,
162) -> Option<&'ll Attribute> {
163 let mut fp = sess.target.frame_pointer;
164 let opts = &sess.opts;
165 if opts.unstable_opts.instrument_mcount {
168 fp.ratchet(FramePointer::Always);
169 }
170 fp.ratchet(opts.cg.force_frame_pointers);
171 let attr_value = match fp {
172 FramePointer::Always => "all",
173 FramePointer::NonLeaf => "non-leaf",
174 FramePointer::MayOmit => return None,
175 };
176 Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value))
177}
178
179fn function_return_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
180 let function_return_attr = match sess.opts.unstable_opts.function_return {
181 FunctionReturn::Keep => return None,
182 FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern,
183 };
184
185 Some(function_return_attr.create_attr(cx.llcx))
186}
187
188#[inline]
190fn instrument_function_attr<'ll>(
191 cx: &SimpleCx<'ll>,
192 sess: &Session,
193) -> SmallVec<[&'ll Attribute; 4]> {
194 let mut attrs = SmallVec::new();
195 if sess.opts.unstable_opts.instrument_mcount {
196 let mcount_name = match &sess.target.llvm_mcount_intrinsic {
202 Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(),
203 None => sess.target.mcount.as_ref(),
204 };
205
206 attrs.push(llvm::CreateAttrStringValue(
207 cx.llcx,
208 "instrument-function-entry-inlined",
209 mcount_name,
210 ));
211 }
212 if let Some(options) = &sess.opts.unstable_opts.instrument_xray {
213 if options.always {
217 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always"));
218 }
219 if options.never {
220 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never"));
221 }
222 if options.ignore_loops {
223 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops"));
224 }
225 let threshold = options.instruction_threshold.unwrap_or(200);
228 attrs.push(llvm::CreateAttrStringValue(
229 cx.llcx,
230 "xray-instruction-threshold",
231 &threshold.to_string(),
232 ));
233 if options.skip_entry {
234 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry"));
235 }
236 if options.skip_exit {
237 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit"));
238 }
239 }
240 attrs
241}
242
243fn nojumptables_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
244 if sess.opts.cg.jump_tables {
245 return None;
246 }
247
248 Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true"))
249}
250
251fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<&'ll Attribute> {
252 if tcx.sess.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) {
256 return None;
257 }
258
259 if tcx.sess.opts.cg.profile_generate.enabled() {
261 return None;
262 }
263
264 let attr_value = match tcx.sess.target.stack_probes {
265 StackProbeType::None => return None,
266 StackProbeType::Inline => "inline-asm",
269 StackProbeType::Call => &mangle_internal_symbol(tcx, "__rust_probestack"),
272 StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
274 if llvm_util::get_version() < min_llvm_version_for_inline {
275 &mangle_internal_symbol(tcx, "__rust_probestack")
276 } else {
277 "inline-asm"
278 }
279 }
280 };
281 Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
282}
283
284fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
285 let sspattr = match sess.stack_protector() {
286 StackProtector::None => return None,
287 StackProtector::All => AttributeKind::StackProtectReq,
288 StackProtector::Strong => AttributeKind::StackProtectStrong,
289 StackProtector::Basic => AttributeKind::StackProtect,
290 };
291
292 Some(sspattr.create_attr(cx.llcx))
293}
294
295fn backchain_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
296 if sess.target.arch != Arch::S390x {
297 return None;
298 }
299
300 let requested_features = sess.opts.cg.target_feature.split(',');
301 let found_positive = requested_features.clone().any(|r| r == "+backchain");
302
303 if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None }
304}
305
306pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute {
307 let target_cpu = llvm_util::target_cpu(sess);
308 llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
309}
310
311pub(crate) fn tune_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> {
312 llvm_util::tune_cpu(sess)
313 .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu))
314}
315
316pub(crate) fn target_features_attr<'ll, 'tcx>(
318 cx: &SimpleCx<'ll>,
319 tcx: TyCtxt<'tcx>,
320 function_features: Vec<String>,
321) -> Option<&'ll Attribute> {
322 let global_features = tcx.global_backend_features(()).iter().map(String::as_str);
323 let function_features = function_features.iter().map(String::as_str);
324 let target_features =
325 global_features.chain(function_features).intersperse(",").collect::<String>();
326 (!target_features.is_empty())
327 .then(|| llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features))
328}
329
330pub(crate) fn non_lazy_bind_attr<'ll>(
333 cx: &SimpleCx<'ll>,
334 sess: &Session,
335) -> Option<&'ll Attribute> {
336 if !sess.needs_plt() { Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) } else { None }
338}
339
340#[inline]
342pub(crate) fn default_optimisation_attrs<'ll>(
343 cx: &SimpleCx<'ll>,
344 sess: &Session,
345) -> SmallVec<[&'ll Attribute; 2]> {
346 let mut attrs = SmallVec::new();
347 match sess.opts.optimize {
348 OptLevel::Size => {
349 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
350 }
351 OptLevel::SizeMin => {
352 attrs.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
353 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
354 }
355 _ => {}
356 }
357 attrs
358}
359
360fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
361 llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
362}
363
364pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
368 cx: &SimpleCx<'ll>,
369 tcx: TyCtxt<'tcx>,
370 llfn: &'ll Value,
371 codegen_fn_attrs: &CodegenFnAttrs,
372 instance: Option<ty::Instance<'tcx>>,
373) {
374 let sess = tcx.sess;
375 let mut to_add = SmallVec::<[_; 16]>::new();
376
377 match codegen_fn_attrs.optimize {
378 OptimizeAttr::Default => {
379 to_add.extend(default_optimisation_attrs(cx, sess));
380 }
381 OptimizeAttr::DoNotOptimize => {
382 to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx));
383 }
384 OptimizeAttr::Size => {
385 to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
386 to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
387 }
388 OptimizeAttr::Speed => {}
389 }
390
391 if sess.must_emit_unwind_tables() {
392 to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind));
393 }
394
395 if sess.opts.unstable_opts.profile_sample_use.is_some() {
396 to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile"));
397 }
398
399 to_add.extend(frame_pointer_type_attr(cx, sess));
401 to_add.extend(function_return_attr(cx, sess));
402 to_add.extend(instrument_function_attr(cx, sess));
403 to_add.extend(nojumptables_attr(cx, sess));
404 to_add.extend(probestack_attr(cx, tcx));
405 to_add.extend(stackprotector_attr(cx, sess));
406
407 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) {
408 to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins"));
409 }
410
411 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
412 to_add.push(AttributeKind::Cold.create_attr(cx.llcx));
413 }
414 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
415 to_add.push(MemoryEffects::ReadOnly.create_attr(cx.llcx));
416 }
417 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
418 to_add.push(MemoryEffects::None.create_attr(cx.llcx));
419 }
420 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
421 } else {
425 to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.sanitizers));
427
428 if let Some(BranchProtection { bti, pac_ret, gcs }) =
430 sess.opts.unstable_opts.branch_protection
431 {
432 assert!(sess.target.arch == Arch::AArch64);
433 if bti {
434 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
435 }
436 if gcs {
437 to_add.push(llvm::CreateAttrString(cx.llcx, "guarded-control-stack"));
438 }
439 if let Some(PacRet { leaf, pc, key }) = pac_ret {
440 if pc {
441 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr"));
442 }
443 to_add.push(llvm::CreateAttrStringValue(
444 cx.llcx,
445 "sign-return-address",
446 if leaf { "all" } else { "non-leaf" },
447 ));
448 to_add.push(llvm::CreateAttrStringValue(
449 cx.llcx,
450 "sign-return-address-key",
451 if key == PAuthKey::A { "a_key" } else { "b_key" },
452 ));
453 }
454 }
455 }
456 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
457 || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
458 {
459 to_add.push(create_alloc_family_attr(cx.llcx));
460 if let Some(instance) = instance
461 && let Some(zv) =
462 tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant)
463 && let Some(name) = zv.value_str()
464 {
465 to_add.push(llvm::CreateAttrStringValue(
466 cx.llcx,
467 "alloc-variant-zeroed",
468 &mangle_internal_symbol(tcx, name.as_str()),
469 ));
470 }
471 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
473 attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);
474 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0));
475 let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned;
476 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
477 flags |= AllocKindFlags::Uninitialized;
478 } else {
479 flags |= AllocKindFlags::Zeroed;
480 }
481 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags));
482 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
485 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
486 }
487 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) {
488 to_add.push(create_alloc_family_attr(cx.llcx));
489 to_add.push(llvm::CreateAllocKindAttr(
490 cx.llcx,
491 AllocKindFlags::Realloc | AllocKindFlags::Aligned,
492 ));
493 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
495 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
496 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
498 attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]);
499 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3));
500 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
501 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
502 }
503 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) {
504 to_add.push(create_alloc_family_attr(cx.llcx));
505 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
506 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
508 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
509 }
510 if let Some(align) = codegen_fn_attrs.alignment {
511 llvm::set_alignment(llfn, align);
512 }
513 if let Some(backchain) = backchain_attr(cx, sess) {
514 to_add.push(backchain);
515 }
516 to_add.extend(patchable_function_entry_attrs(
517 cx,
518 sess,
519 codegen_fn_attrs.patchable_function_entry,
520 ));
521
522 to_add.push(target_cpu_attr(cx, sess));
526 to_add.extend(tune_cpu_attr(cx, sess));
529
530 let function_features =
531 codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
532
533 if function_features.is_empty() {
536 if let Some(instance) = instance
537 && let Some(inline_attr) = inline_attr(cx, tcx, instance)
538 {
539 to_add.push(inline_attr);
540 }
541 }
542
543 let function_features = function_features
544 .iter()
545 .flat_map(|feat| llvm_util::to_llvm_features(sess, feat))
547 .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}")))
549 .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
550 InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
551 InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),
552 }))
553 .collect::<Vec<String>>();
554
555 if sess.target.is_like_wasm {
556 if let Some(instance) = instance
559 && let Some(module) = wasm_import_module(tcx, instance.def_id())
560 {
561 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module));
562
563 let name =
564 codegen_fn_attrs.symbol_name.unwrap_or_else(|| tcx.item_name(instance.def_id()));
565 let name = name.as_str();
566 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
567 }
568 }
569
570 to_add.extend(target_features_attr(cx, tcx, function_features));
571
572 attributes::apply_to_llfn(llfn, Function, &to_add);
573}
574
575fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> {
576 tcx.wasm_import_module_map(id.krate).get(&id)
577}