]>
Commit | Line | Data |
---|---|---|
54a0048b SL |
1 | //! Set and unset common attributes on LLVM values. |
2 | ||
dfeec247 | 3 | use rustc_codegen_ssa::traits::*; |
5e7ed085 | 4 | use rustc_data_structures::small_str::SmallStr; |
29967ef6 | 5 | use rustc_hir::def_id::DefId; |
ba9703b0 | 6 | use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; |
ba9703b0 | 7 | use rustc_middle::ty::{self, TyCtxt}; |
cdc7bbd5 | 8 | use rustc_session::config::OptLevel; |
04454e1e | 9 | use rustc_span::symbol::sym; |
cdc7bbd5 | 10 | use rustc_target::spec::abi::Abi; |
3c0e092e | 11 | use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector}; |
5e7ed085 | 12 | use smallvec::SmallVec; |
041b39d2 | 13 | |
9fa01778 | 14 | use crate::attributes; |
487cf647 | 15 | use crate::errors::{MissingFeatures, SanitizerMemtagRequiresMte, TargetFeatureDisableOrEnable}; |
9fa01778 | 16 | use crate::llvm::AttributePlace::Function; |
487cf647 | 17 | use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects}; |
9fa01778 | 18 | use crate::llvm_util; |
29967ef6 | 19 | pub use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; |
b7449926 | 20 | |
9fa01778 XL |
21 | use crate::context::CodegenCx; |
22 | use crate::value::Value; | |
54a0048b | 23 | |
5e7ed085 FG |
24 | pub fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) { |
25 | if !attrs.is_empty() { | |
26 | llvm::AddFunctionAttributes(llfn, idx, attrs); | |
27 | } | |
28 | } | |
29 | ||
30 | pub fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[&Attribute]) { | |
31 | if !attrs.is_empty() { | |
32 | llvm::AddCallSiteAttributes(callsite, idx, attrs); | |
33 | } | |
34 | } | |
35 | ||
36 | /// Get LLVM attribute for the provided inline heuristic. | |
54a0048b | 37 | #[inline] |
5e7ed085 | 38 | fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll Attribute> { |
f2b60f7d FG |
39 | if !cx.tcx.sess.opts.unstable_opts.inline_llvm { |
40 | // disable LLVM inlining | |
41 | return Some(AttributeKind::NoInline.create_attr(cx.llcx)); | |
42 | } | |
54a0048b | 43 | match inline { |
5e7ed085 FG |
44 | InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)), |
45 | InlineAttr::Always => Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)), | |
46 | InlineAttr::Never => { | |
47 | if cx.sess().target.arch != "amdgpu" { | |
48 | Some(AttributeKind::NoInline.create_attr(cx.llcx)) | |
49 | } else { | |
50 | None | |
b7449926 | 51 | } |
dfeec247 | 52 | } |
5e7ed085 FG |
53 | InlineAttr::None => None, |
54 | } | |
54a0048b SL |
55 | } |
56 | ||
5e7ed085 | 57 | /// Get LLVM sanitize attributes. |
74b04a01 | 58 | #[inline] |
5e7ed085 FG |
59 | pub fn sanitize_attrs<'ll>( |
60 | cx: &CodegenCx<'ll, '_>, | |
61 | no_sanitize: SanitizerSet, | |
62 | ) -> SmallVec<[&'ll Attribute; 4]> { | |
63 | let mut attrs = SmallVec::new(); | |
064997fb | 64 | let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; |
f035d41b | 65 | if enabled.contains(SanitizerSet::ADDRESS) { |
5e7ed085 | 66 | attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); |
f035d41b XL |
67 | } |
68 | if enabled.contains(SanitizerSet::MEMORY) { | |
5e7ed085 | 69 | attrs.push(llvm::AttributeKind::SanitizeMemory.create_attr(cx.llcx)); |
f035d41b XL |
70 | } |
71 | if enabled.contains(SanitizerSet::THREAD) { | |
5e7ed085 | 72 | attrs.push(llvm::AttributeKind::SanitizeThread.create_attr(cx.llcx)); |
74b04a01 | 73 | } |
6a06907d | 74 | if enabled.contains(SanitizerSet::HWADDRESS) { |
5e7ed085 | 75 | attrs.push(llvm::AttributeKind::SanitizeHWAddress.create_attr(cx.llcx)); |
6a06907d | 76 | } |
064997fb FG |
77 | if enabled.contains(SanitizerSet::SHADOWCALLSTACK) { |
78 | attrs.push(llvm::AttributeKind::ShadowCallStack.create_attr(cx.llcx)); | |
79 | } | |
5099ac24 FG |
80 | if enabled.contains(SanitizerSet::MEMTAG) { |
81 | // Check to make sure the mte target feature is actually enabled. | |
5e7ed085 FG |
82 | let features = cx.tcx.global_backend_features(()); |
83 | let mte_feature = | |
84 | features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..])); | |
85 | if let None | Some("-mte") = mte_feature { | |
487cf647 | 86 | cx.tcx.sess.emit_err(SanitizerMemtagRequiresMte); |
5099ac24 FG |
87 | } |
88 | ||
5e7ed085 | 89 | attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx)); |
5099ac24 | 90 | } |
5e7ed085 | 91 | attrs |
74b04a01 XL |
92 | } |
93 | ||
54a0048b SL |
94 | /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. |
95 | #[inline] | |
5e7ed085 | 96 | pub fn uwtable_attr(llcx: &llvm::Context) -> &Attribute { |
5099ac24 FG |
97 | // NOTE: We should determine if we even need async unwind tables, as they |
98 | // take have more overhead and if we can use sync unwind tables we | |
99 | // probably should. | |
5e7ed085 | 100 | llvm::CreateUWTableAttr(llcx, true) |
54a0048b SL |
101 | } |
102 | ||
5e7ed085 | 103 | pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { |
136023e0 | 104 | let mut fp = cx.sess().target.frame_pointer; |
9c376795 | 105 | let opts = &cx.sess().opts; |
136023e0 XL |
106 | // "mcount" function relies on stack pointer. |
107 | // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>. | |
9c376795 | 108 | if opts.unstable_opts.instrument_mcount || matches!(opts.cg.force_frame_pointers, Some(true)) { |
136023e0 | 109 | fp = FramePointer::Always; |
54a0048b | 110 | } |
136023e0 | 111 | let attr_value = match fp { |
5e7ed085 FG |
112 | FramePointer::Always => "all", |
113 | FramePointer::NonLeaf => "non-leaf", | |
114 | FramePointer::MayOmit => return None, | |
136023e0 | 115 | }; |
5e7ed085 | 116 | Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value)) |
3157f602 XL |
117 | } |
118 | ||
0731742a XL |
119 | /// Tell LLVM what instrument function to insert. |
120 | #[inline] | |
5e7ed085 | 121 | fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { |
9c376795 | 122 | if cx.sess().opts.unstable_opts.instrument_mcount { |
0731742a XL |
123 | // Similar to `clang -pg` behavior. Handled by the |
124 | // `post-inline-ee-instrument` LLVM pass. | |
532ac7d7 XL |
125 | |
126 | // The function name varies on platforms. | |
127 | // See test/CodeGen/mcount.c in clang. | |
5e7ed085 | 128 | let mcount_name = cx.sess().target.mcount.as_ref(); |
532ac7d7 | 129 | |
5e7ed085 FG |
130 | Some(llvm::CreateAttrStringValue( |
131 | cx.llcx, | |
132 | "instrument-function-entry-inlined", | |
dfeec247 | 133 | &mcount_name, |
5e7ed085 FG |
134 | )) |
135 | } else { | |
136 | None | |
0731742a XL |
137 | } |
138 | } | |
139 | ||
9c376795 FG |
140 | fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { |
141 | if !cx.sess().opts.unstable_opts.no_jump_tables { | |
142 | return None; | |
143 | } | |
144 | ||
145 | Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true")) | |
146 | } | |
147 | ||
5e7ed085 | 148 | fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { |
041b39d2 | 149 | // Currently stack probes seem somewhat incompatible with the address |
e74abb32 XL |
150 | // sanitizer and thread sanitizer. With asan we're already protected from |
151 | // stack overflow anyway so we don't really need stack probes regardless. | |
f035d41b XL |
152 | if cx |
153 | .sess() | |
154 | .opts | |
064997fb | 155 | .unstable_opts |
f035d41b XL |
156 | .sanitizer |
157 | .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) | |
158 | { | |
5e7ed085 | 159 | return None; |
041b39d2 XL |
160 | } |
161 | ||
dc9dc135 XL |
162 | // probestack doesn't play nice either with `-C profile-generate`. |
163 | if cx.sess().opts.cg.profile_generate.enabled() { | |
5e7ed085 | 164 | return None; |
0531ce1d XL |
165 | } |
166 | ||
8faf50e0 | 167 | // probestack doesn't play nice either with gcov profiling. |
064997fb | 168 | if cx.sess().opts.unstable_opts.profile { |
5e7ed085 | 169 | return None; |
8faf50e0 XL |
170 | } |
171 | ||
5869c6ff | 172 | let attr_value = match cx.sess().target.stack_probes { |
5e7ed085 | 173 | StackProbeType::None => return None, |
5869c6ff XL |
174 | // Request LLVM to generate the probes inline. If the given LLVM version does not support |
175 | // this, no probe is generated at all (even if the attribute is specified). | |
5e7ed085 | 176 | StackProbeType::Inline => "inline-asm", |
5869c6ff XL |
177 | // Flag our internal `__rust_probestack` function as the stack probe symbol. |
178 | // This is defined in the `compiler-builtins` crate for each architecture. | |
5e7ed085 | 179 | StackProbeType::Call => "__rust_probestack", |
5869c6ff XL |
180 | // Pick from the two above based on the LLVM version. |
181 | StackProbeType::InlineOrCall { min_llvm_version_for_inline } => { | |
182 | if llvm_util::get_version() < min_llvm_version_for_inline { | |
5e7ed085 | 183 | "__rust_probestack" |
5869c6ff | 184 | } else { |
5e7ed085 | 185 | "inline-asm" |
5869c6ff XL |
186 | } |
187 | } | |
188 | }; | |
5e7ed085 | 189 | Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value)) |
041b39d2 XL |
190 | } |
191 | ||
5e7ed085 | 192 | fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { |
3c0e092e | 193 | let sspattr = match cx.sess().stack_protector() { |
5e7ed085 FG |
194 | StackProtector::None => return None, |
195 | StackProtector::All => AttributeKind::StackProtectReq, | |
196 | StackProtector::Strong => AttributeKind::StackProtectStrong, | |
197 | StackProtector::Basic => AttributeKind::StackProtect, | |
3c0e092e XL |
198 | }; |
199 | ||
5e7ed085 | 200 | Some(sspattr.create_attr(cx.llcx)) |
3c0e092e XL |
201 | } |
202 | ||
5e7ed085 FG |
203 | pub fn target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Attribute { |
204 | let target_cpu = llvm_util::target_cpu(cx.tcx.sess); | |
205 | llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu) | |
b7449926 XL |
206 | } |
207 | ||
5e7ed085 FG |
208 | pub fn tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { |
209 | llvm_util::tune_cpu(cx.tcx.sess) | |
210 | .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu)) | |
29967ef6 XL |
211 | } |
212 | ||
5e7ed085 FG |
213 | /// Get the `NonLazyBind` LLVM attribute, |
214 | /// if the codegen options allow skipping the PLT. | |
215 | pub fn non_lazy_bind_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { | |
0bf4aa26 | 216 | // Don't generate calls through PLT if it's not necessary |
5e7ed085 FG |
217 | if !cx.sess().needs_plt() { |
218 | Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) | |
219 | } else { | |
220 | None | |
0bf4aa26 XL |
221 | } |
222 | } | |
223 | ||
5e7ed085 FG |
224 | /// Get the default optimizations attrs for a function. |
225 | #[inline] | |
226 | pub(crate) fn default_optimisation_attrs<'ll>( | |
227 | cx: &CodegenCx<'ll, '_>, | |
228 | ) -> SmallVec<[&'ll Attribute; 2]> { | |
229 | let mut attrs = SmallVec::new(); | |
230 | match cx.sess().opts.optimize { | |
9fa01778 | 231 | OptLevel::Size => { |
5e7ed085 | 232 | attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); |
dfeec247 | 233 | } |
9fa01778 | 234 | OptLevel::SizeMin => { |
5e7ed085 FG |
235 | attrs.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx)); |
236 | attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); | |
9fa01778 XL |
237 | } |
238 | _ => {} | |
239 | } | |
5e7ed085 | 240 | attrs |
9fa01778 XL |
241 | } |
242 | ||
064997fb FG |
243 | fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { |
244 | llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") | |
245 | } | |
246 | ||
0731742a | 247 | /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) |
3157f602 | 248 | /// attributes. |
a2a8927a XL |
249 | pub fn from_fn_attrs<'ll, 'tcx>( |
250 | cx: &CodegenCx<'ll, 'tcx>, | |
251 | llfn: &'ll Value, | |
252 | instance: ty::Instance<'tcx>, | |
253 | ) { | |
60c5eb7d | 254 | let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); |
0531ce1d | 255 | |
5e7ed085 FG |
256 | let mut to_add = SmallVec::<[_; 16]>::new(); |
257 | ||
9fa01778 XL |
258 | match codegen_fn_attrs.optimize { |
259 | OptimizeAttr::None => { | |
5e7ed085 | 260 | to_add.extend(default_optimisation_attrs(cx)); |
9fa01778 XL |
261 | } |
262 | OptimizeAttr::Size => { | |
5e7ed085 FG |
263 | to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx)); |
264 | to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); | |
9fa01778 | 265 | } |
5e7ed085 | 266 | OptimizeAttr::Speed => {} |
9fa01778 XL |
267 | } |
268 | ||
487cf647 FG |
269 | let inline = |
270 | if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) { | |
271 | InlineAttr::Hint | |
272 | } else { | |
273 | codegen_fn_attrs.inline | |
274 | }; | |
5e7ed085 | 275 | to_add.extend(inline_attr(cx, inline)); |
b7449926 XL |
276 | |
277 | // The `uwtable` attribute according to LLVM is: | |
278 | // | |
279 | // This attribute indicates that the ABI being targeted requires that an | |
280 | // unwind table entry be produced for this function even if we can show | |
281 | // that no exceptions passes by it. This is normally the case for the | |
282 | // ELF x86-64 abi, but it can be disabled for some compilation units. | |
283 | // | |
284 | // Typically when we're compiling with `-C panic=abort` (which implies this | |
285 | // `no_landing_pads` check) we don't need `uwtable` because we can't | |
286 | // generate any exceptions! On Windows, however, exceptions include other | |
287 | // events such as illegal instructions, segfaults, etc. This means that on | |
288 | // Windows we end up still needing the `uwtable` attribute even if the `-C | |
289 | // panic=abort` flag is passed. | |
290 | // | |
f035d41b | 291 | // You can also find more info on why Windows always requires uwtables here: |
b7449926 | 292 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 |
f9f354fc | 293 | if cx.sess().must_emit_unwind_tables() { |
5e7ed085 | 294 | to_add.push(uwtable_attr(cx.llcx)); |
b7449926 | 295 | } |
3157f602 | 296 | |
064997fb | 297 | if cx.sess().opts.unstable_opts.profile_sample_use.is_some() { |
5e7ed085 | 298 | to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile")); |
c295e0f8 XL |
299 | } |
300 | ||
cdc7bbd5 | 301 | // FIXME: none of these three functions interact with source level attributes. |
5e7ed085 FG |
302 | to_add.extend(frame_pointer_type_attr(cx)); |
303 | to_add.extend(instrument_function_attr(cx)); | |
9c376795 | 304 | to_add.extend(nojumptables_attr(cx)); |
5e7ed085 FG |
305 | to_add.extend(probestack_attr(cx)); |
306 | to_add.extend(stackprotector_attr(cx)); | |
2c00a5a8 | 307 | |
94b46f34 | 308 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { |
5e7ed085 | 309 | to_add.push(AttributeKind::Cold.create_attr(cx.llcx)); |
0531ce1d | 310 | } |
9fa01778 | 311 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) { |
5e7ed085 | 312 | to_add.push(AttributeKind::ReturnsTwice.create_attr(cx.llcx)); |
9fa01778 | 313 | } |
f9f354fc | 314 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) { |
487cf647 | 315 | to_add.push(MemoryEffects::ReadOnly.create_attr(cx.llcx)); |
f9f354fc XL |
316 | } |
317 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) { | |
487cf647 | 318 | to_add.push(MemoryEffects::None.create_attr(cx.llcx)); |
f9f354fc | 319 | } |
94b46f34 | 320 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { |
5e7ed085 | 321 | to_add.push(AttributeKind::Naked.create_attr(cx.llcx)); |
064997fb FG |
322 | // HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions. |
323 | // And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules. | |
324 | // Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768 | |
325 | to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx)); | |
326 | // Need this for AArch64. | |
327 | to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false")); | |
0531ce1d | 328 | } |
064997fb FG |
329 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) |
330 | || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) | |
331 | { | |
332 | if llvm_util::get_version() >= (15, 0, 0) { | |
333 | to_add.push(create_alloc_family_attr(cx.llcx)); | |
334 | // apply to argument place instead of function | |
335 | let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); | |
336 | attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]); | |
337 | to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0)); | |
338 | let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned; | |
339 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { | |
340 | flags |= AllocKindFlags::Uninitialized; | |
341 | } else { | |
342 | flags |= AllocKindFlags::Zeroed; | |
343 | } | |
344 | to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags)); | |
345 | } | |
5e7ed085 FG |
346 | // apply to return place instead of function (unlike all other attributes applied in this function) |
347 | let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); | |
348 | attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); | |
0531ce1d | 349 | } |
064997fb FG |
350 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) { |
351 | if llvm_util::get_version() >= (15, 0, 0) { | |
352 | to_add.push(create_alloc_family_attr(cx.llcx)); | |
353 | to_add.push(llvm::CreateAllocKindAttr( | |
354 | cx.llcx, | |
355 | AllocKindFlags::Realloc | AllocKindFlags::Aligned, | |
356 | )); | |
357 | // applies to argument place instead of function place | |
358 | let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); | |
359 | attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); | |
360 | // apply to argument place instead of function | |
361 | let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); | |
362 | attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]); | |
363 | to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3)); | |
364 | } | |
365 | let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); | |
366 | attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); | |
367 | } | |
368 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) { | |
369 | if llvm_util::get_version() >= (15, 0, 0) { | |
370 | to_add.push(create_alloc_family_attr(cx.llcx)); | |
371 | to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free)); | |
372 | // applies to argument place instead of function place | |
373 | let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); | |
374 | attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); | |
375 | } | |
376 | } | |
1b1a35ee | 377 | if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) { |
5e7ed085 | 378 | to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry")); |
1b1a35ee | 379 | } |
136023e0 XL |
380 | if let Some(align) = codegen_fn_attrs.alignment { |
381 | llvm::set_alignment(llfn, align as usize); | |
382 | } | |
5e7ed085 | 383 | to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); |
94b46f34 | 384 | |
b7449926 XL |
385 | // Always annotate functions with the target-cpu they are compiled for. |
386 | // Without this, ThinLTO won't inline Rust functions into Clang generated | |
387 | // functions (because Clang annotates functions this way too). | |
5e7ed085 | 388 | to_add.push(target_cpu_attr(cx)); |
29967ef6 XL |
389 | // tune-cpu is only conveyed through the attribute for our purpose. |
390 | // The target doesn't care; the subtarget reads our attribute. | |
5e7ed085 | 391 | to_add.extend(tune_cpu_attr(cx)); |
b7449926 | 392 | |
5099ac24 FG |
393 | let function_features = |
394 | codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::<Vec<&str>>(); | |
395 | ||
396 | if let Some(f) = llvm_util::check_tied_features( | |
397 | cx.tcx.sess, | |
398 | &function_features.iter().map(|f| (*f, true)).collect(), | |
399 | ) { | |
400 | let span = cx | |
401 | .tcx | |
f2b60f7d FG |
402 | .get_attrs(instance.def_id(), sym::target_feature) |
403 | .next() | |
5099ac24 | 404 | .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span); |
487cf647 FG |
405 | cx.tcx |
406 | .sess | |
407 | .create_err(TargetFeatureDisableOrEnable { | |
408 | features: f, | |
409 | span: Some(span), | |
410 | missing_features: Some(MissingFeatures), | |
411 | }) | |
412 | .emit(); | |
5099ac24 FG |
413 | return; |
414 | } | |
415 | ||
416 | let mut function_features = function_features | |
6a06907d | 417 | .iter() |
5099ac24 | 418 | .flat_map(|feat| { |
5e7ed085 | 419 | llvm_util::to_llvm_features(cx.tcx.sess, feat).into_iter().map(|f| format!("+{}", f)) |
6a06907d | 420 | }) |
29967ef6 XL |
421 | .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { |
422 | InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(), | |
423 | InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(), | |
424 | })) | |
6a06907d | 425 | .collect::<Vec<String>>(); |
0531ce1d | 426 | |
cdc7bbd5 XL |
427 | if cx.tcx.sess.target.is_like_wasm { |
428 | // If this function is an import from the environment but the wasm | |
429 | // import has a specific module/name, apply them here. | |
60c5eb7d | 430 | if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) { |
5e7ed085 | 431 | to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", &module)); |
dfeec247 XL |
432 | |
433 | let name = | |
434 | codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id())); | |
5e7ed085 FG |
435 | let name = name.as_str(); |
436 | to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name)); | |
0531ce1d | 437 | } |
cdc7bbd5 XL |
438 | |
439 | // The `"wasm"` abi on wasm targets automatically enables the | |
440 | // `+multivalue` feature because the purpose of the wasm abi is to match | |
441 | // the WebAssembly specification, which has this feature. This won't be | |
442 | // needed when LLVM enables this `multivalue` feature by default. | |
443 | if !cx.tcx.is_closure(instance.def_id()) { | |
444 | let abi = cx.tcx.fn_sig(instance.def_id()).abi(); | |
445 | if abi == Abi::Wasm { | |
446 | function_features.push("+multivalue".to_string()); | |
447 | } | |
448 | } | |
449 | } | |
450 | ||
5e7ed085 FG |
451 | let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str()); |
452 | let function_features = function_features.iter().map(|s| s.as_str()); | |
453 | let target_features = | |
454 | global_features.chain(function_features).intersperse(",").collect::<SmallStr<1024>>(); | |
455 | if !target_features.is_empty() { | |
456 | to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features)); | |
0531ce1d | 457 | } |
5e7ed085 FG |
458 | |
459 | attributes::apply_to_llfn(llfn, Function, &to_add); | |
476ff2be SL |
460 | } |
461 | ||
5e7ed085 FG |
462 | fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> { |
463 | tcx.wasm_import_module_map(id.krate).get(&id) | |
0531ce1d | 464 | } |