1use rustc_attr_data_structures::{InlineAttr, InstructionSetAttr, OptimizeAttr};
3use rustc_codegen_ssa::traits::*;
4use rustc_hir::def_id::DefId;
5use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
6use rustc_middle::ty::{self, TyCtxt};
7use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
8use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
9use smallvec::SmallVec;
10
11use crate::context::CodegenCx;
12use crate::errors::SanitizerMemtagRequiresMte;
13use crate::llvm::AttributePlace::Function;
14use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects};
15use crate::value::Value;
16use crate::{attributes, llvm_util};
17
18pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
19 if !attrs.is_empty() {
20 llvm::AddFunctionAttributes(llfn, idx, attrs);
21 }
22}
23
24pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
25 if !attrs.is_empty() {
26 llvm::AddCallSiteAttributes(callsite, idx, attrs);
27 }
28}
29
30pub(crate) fn has_attr(llfn: &Value, idx: AttributePlace, attr: AttributeKind) -> bool {
31 llvm::HasAttributeAtIndex(llfn, idx, attr)
32}
33
34pub(crate) fn has_string_attr(llfn: &Value, name: &str) -> bool {
35 llvm::HasStringAttribute(llfn, name)
36}
37
38pub(crate) fn remove_from_llfn(llfn: &Value, place: AttributePlace, kind: AttributeKind) {
39 llvm::RemoveRustEnumAttributeAtIndex(llfn, place, kind);
40}
41
42pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) {
43 llvm::RemoveStringAttrFromFn(llfn, name);
44}
45
46#[inline]
48fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll Attribute> {
49 if !cx.tcx.sess.opts.unstable_opts.inline_llvm {
50 return Some(AttributeKind::NoInline.create_attr(cx.llcx));
52 }
53 match inline {
54 InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)),
55 InlineAttr::Always | InlineAttr::Force { .. } => {
56 Some(AttributeKind::AlwaysInline.create_attr(cx.llcx))
57 }
58 InlineAttr::Never => {
59 if cx.sess().target.arch != "amdgpu" {
60 Some(AttributeKind::NoInline.create_attr(cx.llcx))
61 } else {
62 None
63 }
64 }
65 InlineAttr::None => None,
66 }
67}
68
69#[inline]
70fn patchable_function_entry_attrs<'ll>(
71 cx: &CodegenCx<'ll, '_>,
72 attr: Option<PatchableFunctionEntry>,
73) -> SmallVec<[&'ll Attribute; 2]> {
74 let mut attrs = SmallVec::new();
75 let patchable_spec = attr.unwrap_or_else(|| {
76 PatchableFunctionEntry::from_config(cx.tcx.sess.opts.unstable_opts.patchable_function_entry)
77 });
78 let entry = patchable_spec.entry();
79 let prefix = patchable_spec.prefix();
80 if entry > 0 {
81 attrs.push(llvm::CreateAttrStringValue(
82 cx.llcx,
83 "patchable-function-entry",
84 &format!("{}", entry),
85 ));
86 }
87 if prefix > 0 {
88 attrs.push(llvm::CreateAttrStringValue(
89 cx.llcx,
90 "patchable-function-prefix",
91 &format!("{}", prefix),
92 ));
93 }
94 attrs
95}
96
97#[inline]
99pub(crate) fn sanitize_attrs<'ll>(
100 cx: &CodegenCx<'ll, '_>,
101 no_sanitize: SanitizerSet,
102) -> SmallVec<[&'ll Attribute; 4]> {
103 let mut attrs = SmallVec::new();
104 let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
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 = cx.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 cx.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 attrs
135}
136
137#[inline]
139pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option<bool>) -> &Attribute {
140 let async_unwind = !use_sync_unwind.unwrap_or(false);
144 llvm::CreateUWTableAttr(llcx, async_unwind)
145}
146
147pub(crate) fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
148 let mut fp = cx.sess().target.frame_pointer;
149 let opts = &cx.sess().opts;
150 if opts.unstable_opts.instrument_mcount {
153 fp.ratchet(FramePointer::Always);
154 }
155 fp.ratchet(opts.cg.force_frame_pointers);
156 let attr_value = match fp {
157 FramePointer::Always => "all",
158 FramePointer::NonLeaf => "non-leaf",
159 FramePointer::MayOmit => return None,
160 };
161 Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value))
162}
163
164fn function_return_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
165 let function_return_attr = match cx.sess().opts.unstable_opts.function_return {
166 FunctionReturn::Keep => return None,
167 FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern,
168 };
169
170 Some(function_return_attr.create_attr(cx.llcx))
171}
172
173#[inline]
175fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> {
176 let mut attrs = SmallVec::new();
177 if cx.sess().opts.unstable_opts.instrument_mcount {
178 let mcount_name = match &cx.sess().target.llvm_mcount_intrinsic {
184 Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(),
185 None => cx.sess().target.mcount.as_ref(),
186 };
187
188 attrs.push(llvm::CreateAttrStringValue(
189 cx.llcx,
190 "instrument-function-entry-inlined",
191 mcount_name,
192 ));
193 }
194 if let Some(options) = &cx.sess().opts.unstable_opts.instrument_xray {
195 if options.always {
199 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always"));
200 }
201 if options.never {
202 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never"));
203 }
204 if options.ignore_loops {
205 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops"));
206 }
207 let threshold = options.instruction_threshold.unwrap_or(200);
210 attrs.push(llvm::CreateAttrStringValue(
211 cx.llcx,
212 "xray-instruction-threshold",
213 &threshold.to_string(),
214 ));
215 if options.skip_entry {
216 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry"));
217 }
218 if options.skip_exit {
219 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit"));
220 }
221 }
222 attrs
223}
224
225fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
226 if !cx.sess().opts.unstable_opts.no_jump_tables {
227 return None;
228 }
229
230 Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true"))
231}
232
233fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
234 if cx
238 .sess()
239 .opts
240 .unstable_opts
241 .sanitizer
242 .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
243 {
244 return None;
245 }
246
247 if cx.sess().opts.cg.profile_generate.enabled() {
249 return None;
250 }
251
252 let attr_value = match cx.sess().target.stack_probes {
253 StackProbeType::None => return None,
254 StackProbeType::Inline => "inline-asm",
257 StackProbeType::Call => "__rust_probestack",
260 StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
262 if llvm_util::get_version() < min_llvm_version_for_inline {
263 "__rust_probestack"
264 } else {
265 "inline-asm"
266 }
267 }
268 };
269 Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
270}
271
272fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
273 let sspattr = match cx.sess().stack_protector() {
274 StackProtector::None => return None,
275 StackProtector::All => AttributeKind::StackProtectReq,
276 StackProtector::Strong => AttributeKind::StackProtectStrong,
277 StackProtector::Basic => AttributeKind::StackProtect,
278 };
279
280 Some(sspattr.create_attr(cx.llcx))
281}
282
283fn backchain_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
284 if cx.sess().target.arch != "s390x" {
285 return None;
286 }
287
288 let requested_features = cx.sess().opts.cg.target_feature.split(',');
289 let found_positive = requested_features.clone().any(|r| r == "+backchain");
290
291 if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None }
292}
293
294pub(crate) fn target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Attribute {
295 let target_cpu = llvm_util::target_cpu(cx.tcx.sess);
296 llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
297}
298
299pub(crate) fn tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
300 llvm_util::tune_cpu(cx.tcx.sess)
301 .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu))
302}
303
304pub(crate) fn non_lazy_bind_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
307 if !cx.sess().needs_plt() {
309 Some(AttributeKind::NonLazyBind.create_attr(cx.llcx))
310 } else {
311 None
312 }
313}
314
315#[inline]
317pub(crate) fn default_optimisation_attrs<'ll>(
318 cx: &CodegenCx<'ll, '_>,
319) -> SmallVec<[&'ll Attribute; 2]> {
320 let mut attrs = SmallVec::new();
321 match cx.sess().opts.optimize {
322 OptLevel::Size => {
323 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
324 }
325 OptLevel::SizeMin => {
326 attrs.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
327 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
328 }
329 _ => {}
330 }
331 attrs
332}
333
334fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
335 llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
336}
337
338pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
342 cx: &CodegenCx<'ll, 'tcx>,
343 llfn: &'ll Value,
344 instance: ty::Instance<'tcx>,
345) {
346 let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
347
348 let mut to_add = SmallVec::<[_; 16]>::new();
349
350 match codegen_fn_attrs.optimize {
351 OptimizeAttr::Default => {
352 to_add.extend(default_optimisation_attrs(cx));
353 }
354 OptimizeAttr::DoNotOptimize => {
355 to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx));
356 }
357 OptimizeAttr::Size => {
358 to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
359 to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
360 }
361 OptimizeAttr::Speed => {}
362 }
363
364 let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
366 (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
367 (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint,
368 (inline, _) => inline,
369 };
370 to_add.extend(inline_attr(cx, inline));
371
372 if cx.sess().must_emit_unwind_tables() {
389 to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind));
390 }
391
392 if cx.sess().opts.unstable_opts.profile_sample_use.is_some() {
393 to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile"));
394 }
395
396 to_add.extend(frame_pointer_type_attr(cx));
398 to_add.extend(function_return_attr(cx));
399 to_add.extend(instrument_function_attr(cx));
400 to_add.extend(nojumptables_attr(cx));
401 to_add.extend(probestack_attr(cx));
402 to_add.extend(stackprotector_attr(cx));
403
404 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) {
405 to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins"));
406 }
407
408 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
409 to_add.push(AttributeKind::Cold.create_attr(cx.llcx));
410 }
411 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
412 to_add.push(MemoryEffects::ReadOnly.create_attr(cx.llcx));
413 }
414 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
415 to_add.push(MemoryEffects::None.create_attr(cx.llcx));
416 }
417 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
418 } else {
422 to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
424
425 if let Some(BranchProtection { bti, pac_ret }) =
427 cx.sess().opts.unstable_opts.branch_protection
428 {
429 assert!(cx.sess().target.arch == "aarch64");
430 if bti {
431 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
432 }
433 if let Some(PacRet { leaf, pc, key }) = pac_ret {
434 if pc {
435 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr"));
436 }
437 to_add.push(llvm::CreateAttrStringValue(
438 cx.llcx,
439 "sign-return-address",
440 if leaf { "all" } else { "non-leaf" },
441 ));
442 to_add.push(llvm::CreateAttrStringValue(
443 cx.llcx,
444 "sign-return-address-key",
445 if key == PAuthKey::A { "a_key" } else { "b_key" },
446 ));
447 }
448 }
449 }
450 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
451 || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
452 {
453 to_add.push(create_alloc_family_attr(cx.llcx));
454 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
456 attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);
457 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0));
458 let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned;
459 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
460 flags |= AllocKindFlags::Uninitialized;
461 } else {
462 flags |= AllocKindFlags::Zeroed;
463 }
464 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags));
465 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
468 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
469 }
470 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) {
471 to_add.push(create_alloc_family_attr(cx.llcx));
472 to_add.push(llvm::CreateAllocKindAttr(
473 cx.llcx,
474 AllocKindFlags::Realloc | AllocKindFlags::Aligned,
475 ));
476 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
478 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
479 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
481 attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]);
482 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3));
483 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
484 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
485 }
486 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) {
487 to_add.push(create_alloc_family_attr(cx.llcx));
488 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
489 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
491 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
492 }
493 if let Some(align) =
496 Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment)
497 {
498 llvm::set_alignment(llfn, align);
499 }
500 if let Some(backchain) = backchain_attr(cx) {
501 to_add.push(backchain);
502 }
503 to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry));
504
505 to_add.push(target_cpu_attr(cx));
509 to_add.extend(tune_cpu_attr(cx));
512
513 let function_features =
514 codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
515
516 let function_features = function_features
517 .iter()
518 .flat_map(|feat| llvm_util::to_llvm_features(cx.tcx.sess, feat))
520 .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}")))
522 .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
523 InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
524 InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),
525 }))
526 .collect::<Vec<String>>();
527
528 if cx.tcx.sess.target.is_like_wasm {
529 if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
532 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module));
533
534 let name =
535 codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
536 let name = name.as_str();
537 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
538 }
539 }
540
541 let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
542 let function_features = function_features.iter().map(|s| s.as_str());
543 let target_features: String =
544 global_features.chain(function_features).intersperse(",").collect();
545 if !target_features.is_empty() {
546 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features));
547 }
548
549 attributes::apply_to_llfn(llfn, Function, &to_add);
550}
551
552fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> {
553 tcx.wasm_import_module_map(id.krate).get(&id)
554}