1use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr};
4use rustc_codegen_ssa::traits::*;
5use rustc_hir::def_id::DefId;
6use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
7use rustc_middle::ty::{self, TyCtxt};
8use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
9use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
10use smallvec::SmallVec;
11
12use crate::context::CodegenCx;
13use crate::errors::SanitizerMemtagRequiresMte;
14use crate::llvm::AttributePlace::Function;
15use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects};
16use crate::value::Value;
17use crate::{attributes, llvm_util};
18
19pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
20 if !attrs.is_empty() {
21 llvm::AddFunctionAttributes(llfn, idx, attrs);
22 }
23}
24
25pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
26 if !attrs.is_empty() {
27 llvm::AddCallSiteAttributes(callsite, idx, attrs);
28 }
29}
30
31#[inline]
33fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll Attribute> {
34 if !cx.tcx.sess.opts.unstable_opts.inline_llvm {
35 return Some(AttributeKind::NoInline.create_attr(cx.llcx));
37 }
38 match inline {
39 InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)),
40 InlineAttr::Always | InlineAttr::Force { .. } => {
41 Some(AttributeKind::AlwaysInline.create_attr(cx.llcx))
42 }
43 InlineAttr::Never => {
44 if cx.sess().target.arch != "amdgpu" {
45 Some(AttributeKind::NoInline.create_attr(cx.llcx))
46 } else {
47 None
48 }
49 }
50 InlineAttr::None => None,
51 }
52}
53
54#[inline]
55fn patchable_function_entry_attrs<'ll>(
56 cx: &CodegenCx<'ll, '_>,
57 attr: Option<PatchableFunctionEntry>,
58) -> SmallVec<[&'ll Attribute; 2]> {
59 let mut attrs = SmallVec::new();
60 let patchable_spec = attr.unwrap_or_else(|| {
61 PatchableFunctionEntry::from_config(cx.tcx.sess.opts.unstable_opts.patchable_function_entry)
62 });
63 let entry = patchable_spec.entry();
64 let prefix = patchable_spec.prefix();
65 if entry > 0 {
66 attrs.push(llvm::CreateAttrStringValue(
67 cx.llcx,
68 "patchable-function-entry",
69 &format!("{}", entry),
70 ));
71 }
72 if prefix > 0 {
73 attrs.push(llvm::CreateAttrStringValue(
74 cx.llcx,
75 "patchable-function-prefix",
76 &format!("{}", prefix),
77 ));
78 }
79 attrs
80}
81
82#[inline]
84pub(crate) fn sanitize_attrs<'ll>(
85 cx: &CodegenCx<'ll, '_>,
86 no_sanitize: SanitizerSet,
87) -> SmallVec<[&'ll Attribute; 4]> {
88 let mut attrs = SmallVec::new();
89 let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
90 if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
91 attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
92 }
93 if enabled.contains(SanitizerSet::MEMORY) {
94 attrs.push(llvm::AttributeKind::SanitizeMemory.create_attr(cx.llcx));
95 }
96 if enabled.contains(SanitizerSet::THREAD) {
97 attrs.push(llvm::AttributeKind::SanitizeThread.create_attr(cx.llcx));
98 }
99 if enabled.contains(SanitizerSet::HWADDRESS) {
100 attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx));
101 }
102 if enabled.contains(SanitizerSet::SHADOWCALLSTACK) {
103 attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx));
104 }
105 if enabled.contains(SanitizerSet::MEMTAG) {
106 let features = cx.tcx.global_backend_features(());
108 let mte_feature =
109 features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..]));
110 if let None | Some("-mte") = mte_feature {
111 cx.tcx.dcx().emit_err(SanitizerMemtagRequiresMte);
112 }
113
114 attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
115 }
116 if enabled.contains(SanitizerSet::SAFESTACK) {
117 attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
118 }
119 attrs
120}
121
122#[inline]
124pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option<bool>) -> &Attribute {
125 let async_unwind = !use_sync_unwind.unwrap_or(false);
129 llvm::CreateUWTableAttr(llcx, async_unwind)
130}
131
132pub(crate) fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
133 let mut fp = cx.sess().target.frame_pointer;
134 let opts = &cx.sess().opts;
135 if opts.unstable_opts.instrument_mcount {
138 fp.ratchet(FramePointer::Always);
139 }
140 fp.ratchet(opts.cg.force_frame_pointers);
141 let attr_value = match fp {
142 FramePointer::Always => "all",
143 FramePointer::NonLeaf => "non-leaf",
144 FramePointer::MayOmit => return None,
145 };
146 Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value))
147}
148
149fn function_return_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
150 let function_return_attr = match cx.sess().opts.unstable_opts.function_return {
151 FunctionReturn::Keep => return None,
152 FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern,
153 };
154
155 Some(function_return_attr.create_attr(cx.llcx))
156}
157
158#[inline]
160fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> {
161 let mut attrs = SmallVec::new();
162 if cx.sess().opts.unstable_opts.instrument_mcount {
163 let mcount_name = match &cx.sess().target.llvm_mcount_intrinsic {
169 Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(),
170 None => cx.sess().target.mcount.as_ref(),
171 };
172
173 attrs.push(llvm::CreateAttrStringValue(
174 cx.llcx,
175 "instrument-function-entry-inlined",
176 mcount_name,
177 ));
178 }
179 if let Some(options) = &cx.sess().opts.unstable_opts.instrument_xray {
180 if options.always {
184 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always"));
185 }
186 if options.never {
187 attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never"));
188 }
189 if options.ignore_loops {
190 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops"));
191 }
192 let threshold = options.instruction_threshold.unwrap_or(200);
195 attrs.push(llvm::CreateAttrStringValue(
196 cx.llcx,
197 "xray-instruction-threshold",
198 &threshold.to_string(),
199 ));
200 if options.skip_entry {
201 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry"));
202 }
203 if options.skip_exit {
204 attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit"));
205 }
206 }
207 attrs
208}
209
210fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
211 if !cx.sess().opts.unstable_opts.no_jump_tables {
212 return None;
213 }
214
215 Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true"))
216}
217
218fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
219 if cx
223 .sess()
224 .opts
225 .unstable_opts
226 .sanitizer
227 .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
228 {
229 return None;
230 }
231
232 if cx.sess().opts.cg.profile_generate.enabled() {
234 return None;
235 }
236
237 let attr_value = match cx.sess().target.stack_probes {
238 StackProbeType::None => return None,
239 StackProbeType::Inline => "inline-asm",
242 StackProbeType::Call => "__rust_probestack",
245 StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
247 if llvm_util::get_version() < min_llvm_version_for_inline {
248 "__rust_probestack"
249 } else {
250 "inline-asm"
251 }
252 }
253 };
254 Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
255}
256
257fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
258 let sspattr = match cx.sess().stack_protector() {
259 StackProtector::None => return None,
260 StackProtector::All => AttributeKind::StackProtectReq,
261 StackProtector::Strong => AttributeKind::StackProtectStrong,
262 StackProtector::Basic => AttributeKind::StackProtect,
263 };
264
265 Some(sspattr.create_attr(cx.llcx))
266}
267
268fn backchain_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
269 if cx.sess().target.arch != "s390x" {
270 return None;
271 }
272
273 let requested_features = cx.sess().opts.cg.target_feature.split(',');
274 let found_positive = requested_features.clone().any(|r| r == "+backchain");
275
276 if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None }
277}
278
279pub(crate) fn target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Attribute {
280 let target_cpu = llvm_util::target_cpu(cx.tcx.sess);
281 llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu)
282}
283
284pub(crate) fn tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
285 llvm_util::tune_cpu(cx.tcx.sess)
286 .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu))
287}
288
289pub(crate) fn non_lazy_bind_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
292 if !cx.sess().needs_plt() {
294 Some(AttributeKind::NonLazyBind.create_attr(cx.llcx))
295 } else {
296 None
297 }
298}
299
300#[inline]
302pub(crate) fn default_optimisation_attrs<'ll>(
303 cx: &CodegenCx<'ll, '_>,
304) -> SmallVec<[&'ll Attribute; 2]> {
305 let mut attrs = SmallVec::new();
306 match cx.sess().opts.optimize {
307 OptLevel::Size => {
308 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
309 }
310 OptLevel::SizeMin => {
311 attrs.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
312 attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
313 }
314 _ => {}
315 }
316 attrs
317}
318
319fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
320 llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
321}
322
323pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
327 cx: &CodegenCx<'ll, 'tcx>,
328 llfn: &'ll Value,
329 instance: ty::Instance<'tcx>,
330) {
331 let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
332
333 let mut to_add = SmallVec::<[_; 16]>::new();
334
335 match codegen_fn_attrs.optimize {
336 OptimizeAttr::Default => {
337 to_add.extend(default_optimisation_attrs(cx));
338 }
339 OptimizeAttr::DoNotOptimize => {
340 to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx));
341 }
342 OptimizeAttr::Size => {
343 to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
344 to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
345 }
346 OptimizeAttr::Speed => {}
347 }
348
349 let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
351 (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
352 (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint,
353 (inline, _) => inline,
354 };
355 to_add.extend(inline_attr(cx, inline));
356
357 if cx.sess().must_emit_unwind_tables() {
374 to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind));
375 }
376
377 if cx.sess().opts.unstable_opts.profile_sample_use.is_some() {
378 to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile"));
379 }
380
381 to_add.extend(frame_pointer_type_attr(cx));
383 to_add.extend(function_return_attr(cx));
384 to_add.extend(instrument_function_attr(cx));
385 to_add.extend(nojumptables_attr(cx));
386 to_add.extend(probestack_attr(cx));
387 to_add.extend(stackprotector_attr(cx));
388
389 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) {
390 to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins"));
391 }
392
393 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
394 to_add.push(AttributeKind::Cold.create_attr(cx.llcx));
395 }
396 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
397 to_add.push(MemoryEffects::ReadOnly.create_attr(cx.llcx));
398 }
399 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
400 to_add.push(MemoryEffects::None.create_attr(cx.llcx));
401 }
402 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
403 } else {
407 to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
409
410 if llvm_util::get_version() >= (19, 0, 0) {
411 if let Some(BranchProtection { bti, pac_ret }) =
413 cx.sess().opts.unstable_opts.branch_protection
414 {
415 assert!(cx.sess().target.arch == "aarch64");
416 if bti {
417 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement"));
418 }
419 if let Some(PacRet { leaf, pc, key }) = pac_ret {
420 if pc {
421 to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr"));
422 }
423 to_add.push(llvm::CreateAttrStringValue(
424 cx.llcx,
425 "sign-return-address",
426 if leaf { "all" } else { "non-leaf" },
427 ));
428 to_add.push(llvm::CreateAttrStringValue(
429 cx.llcx,
430 "sign-return-address-key",
431 if key == PAuthKey::A { "a_key" } else { "b_key" },
432 ));
433 }
434 }
435 }
436 }
437 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
438 || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
439 {
440 to_add.push(create_alloc_family_attr(cx.llcx));
441 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
443 attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);
444 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0));
445 let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned;
446 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
447 flags |= AllocKindFlags::Uninitialized;
448 } else {
449 flags |= AllocKindFlags::Zeroed;
450 }
451 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags));
452 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
455 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
456 }
457 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) {
458 to_add.push(create_alloc_family_attr(cx.llcx));
459 to_add.push(llvm::CreateAllocKindAttr(
460 cx.llcx,
461 AllocKindFlags::Realloc | AllocKindFlags::Aligned,
462 ));
463 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
465 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
466 let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
468 attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]);
469 to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3));
470 let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
471 attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
472 }
473 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) {
474 to_add.push(create_alloc_family_attr(cx.llcx));
475 to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
476 let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
478 attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
479 }
480 if let Some(align) =
483 Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment)
484 {
485 llvm::set_alignment(llfn, align);
486 }
487 if let Some(backchain) = backchain_attr(cx) {
488 to_add.push(backchain);
489 }
490 to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry));
491
492 to_add.push(target_cpu_attr(cx));
496 to_add.extend(tune_cpu_attr(cx));
499
500 let function_features =
501 codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>();
502
503 let function_features = function_features
504 .iter()
505 .flat_map(|feat| llvm_util::to_llvm_features(cx.tcx.sess, feat))
507 .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}")))
509 .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
510 InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
511 InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),
512 }))
513 .chain(match &*cx.tcx.sess.target.arch {
516 "aarch64" if llvm_util::get_version().0 == 18 => vec!["+fpmr".to_string()],
517 _ => vec![],
518 })
519 .collect::<Vec<String>>();
520
521 if cx.tcx.sess.target.is_like_wasm {
522 if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
525 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module));
526
527 let name =
528 codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
529 let name = name.as_str();
530 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name));
531 }
532 }
533
534 let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
535 let function_features = function_features.iter().map(|s| s.as_str());
536 let target_features: String =
537 global_features.chain(function_features).intersperse(",").collect();
538 if !target_features.is_empty() {
539 to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features));
540 }
541
542 attributes::apply_to_llfn(llfn, Function, &to_add);
543}
544
545fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> {
546 tcx.wasm_import_module_map(id.krate).get(&id)
547}