]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_llvm/src/attributes.rs
New upstream version 1.51.0+dfsg1
[rustc.git] / compiler / rustc_codegen_llvm / src / attributes.rs
CommitLineData
54a0048b
SL
1//! Set and unset common attributes on LLVM values.
2
b7449926 3use std::ffi::CString;
476ff2be 4
dfeec247
XL
5use rustc_codegen_ssa::traits::*;
6use rustc_data_structures::const_cstr;
0531ce1d 7use rustc_data_structures::fx::FxHashMap;
dfeec247 8use rustc_data_structures::small_c_str::SmallCStr;
29967ef6 9use rustc_hir::def_id::DefId;
ba9703b0
XL
10use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
11use rustc_middle::ty::layout::HasTyCtxt;
12use rustc_middle::ty::query::Providers;
13use rustc_middle::ty::{self, TyCtxt};
f035d41b 14use rustc_session::config::{OptLevel, SanitizerSet};
ba9703b0 15use rustc_session::Session;
5869c6ff 16use rustc_target::spec::StackProbeType;
041b39d2 17
9fa01778 18use crate::attributes;
9fa01778 19use crate::llvm::AttributePlace::Function;
dfeec247 20use crate::llvm::{self, Attribute};
9fa01778 21use crate::llvm_util;
29967ef6 22pub use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr};
b7449926 23
9fa01778
XL
24use crate::context::CodegenCx;
25use crate::value::Value;
54a0048b
SL
26
27/// Mark LLVM function to use provided inline heuristic.
28#[inline]
60c5eb7d 29fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
54a0048b
SL
30 use self::InlineAttr::*;
31 match inline {
dfeec247 32 Hint => Attribute::InlineHint.apply_llfn(Function, val),
5bcae85e 33 Always => Attribute::AlwaysInline.apply_llfn(Function, val),
dfeec247 34 Never => {
29967ef6 35 if cx.tcx().sess.target.arch != "amdgpu" {
b7449926
XL
36 Attribute::NoInline.apply_llfn(Function, val);
37 }
dfeec247 38 }
fc512014 39 None => {}
54a0048b
SL
40 };
41}
42
74b04a01
XL
43/// Apply LLVM sanitize attributes.
44#[inline]
f035d41b
XL
45pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll Value) {
46 let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize;
47 if enabled.contains(SanitizerSet::ADDRESS) {
48 llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
49 }
50 if enabled.contains(SanitizerSet::MEMORY) {
51 llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
52 }
53 if enabled.contains(SanitizerSet::THREAD) {
54 llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
74b04a01
XL
55 }
56}
57
54a0048b
SL
58/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
59#[inline]
b7449926 60pub fn emit_uwtable(val: &'ll Value, emit: bool) {
5bcae85e 61 Attribute::UWTable.toggle_llfn(Function, val, emit);
54a0048b
SL
62}
63
0731742a 64/// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
54a0048b 65#[inline]
60c5eb7d 66fn naked(val: &'ll Value, is_naked: bool) {
5bcae85e 67 Attribute::Naked.toggle_llfn(Function, val, is_naked);
54a0048b
SL
68}
69
b7449926 70pub fn set_frame_pointer_elimination(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
2c00a5a8 71 if cx.sess().must_not_eliminate_frame_pointers() {
ba9703b0
XL
72 llvm::AddFunctionAttrStringValue(
73 llfn,
74 llvm::AttributePlace::Function,
75 const_cstr!("frame-pointer"),
76 const_cstr!("all"),
77 );
54a0048b 78 }
3157f602
XL
79}
80
0731742a
XL
81/// Tell LLVM what instrument function to insert.
82#[inline]
60c5eb7d 83fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
0731742a
XL
84 if cx.sess().instrument_mcount() {
85 // Similar to `clang -pg` behavior. Handled by the
86 // `post-inline-ee-instrument` LLVM pass.
532ac7d7
XL
87
88 // The function name varies on platforms.
89 // See test/CodeGen/mcount.c in clang.
29967ef6 90 let mcount_name = CString::new(cx.sess().target.mcount.as_str().as_bytes()).unwrap();
532ac7d7 91
0731742a 92 llvm::AddFunctionAttrStringValue(
dfeec247
XL
93 llfn,
94 llvm::AttributePlace::Function,
95 const_cstr!("instrument-function-entry-inlined"),
96 &mcount_name,
97 );
0731742a
XL
98 }
99}
100
60c5eb7d 101fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
041b39d2 102 // Currently stack probes seem somewhat incompatible with the address
e74abb32
XL
103 // sanitizer and thread sanitizer. With asan we're already protected from
104 // stack overflow anyway so we don't really need stack probes regardless.
f035d41b
XL
105 if cx
106 .sess()
107 .opts
108 .debugging_opts
109 .sanitizer
110 .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
111 {
112 return;
041b39d2
XL
113 }
114
dc9dc135
XL
115 // probestack doesn't play nice either with `-C profile-generate`.
116 if cx.sess().opts.cg.profile_generate.enabled() {
0531ce1d
XL
117 return;
118 }
119
8faf50e0
XL
120 // probestack doesn't play nice either with gcov profiling.
121 if cx.sess().opts.debugging_opts.profile {
122 return;
123 }
124
5869c6ff
XL
125 let attr_value = match cx.sess().target.stack_probes {
126 StackProbeType::None => None,
127 // Request LLVM to generate the probes inline. If the given LLVM version does not support
128 // this, no probe is generated at all (even if the attribute is specified).
129 StackProbeType::Inline => Some(const_cstr!("inline-asm")),
130 // Flag our internal `__rust_probestack` function as the stack probe symbol.
131 // This is defined in the `compiler-builtins` crate for each architecture.
132 StackProbeType::Call => Some(const_cstr!("__rust_probestack")),
133 // Pick from the two above based on the LLVM version.
134 StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
135 if llvm_util::get_version() < min_llvm_version_for_inline {
136 Some(const_cstr!("__rust_probestack"))
137 } else {
138 Some(const_cstr!("inline-asm"))
139 }
140 }
141 };
142 if let Some(attr_value) = attr_value {
143 llvm::AddFunctionAttrStringValue(
144 llfn,
145 llvm::AttributePlace::Function,
146 const_cstr!("probe-stack"),
147 attr_value,
148 );
149 }
041b39d2
XL
150}
151
83c7162d 152pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {
dfeec247 153 const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
83c7162d 154
dfeec247
XL
155 let cmdline = sess
156 .opts
157 .cg
158 .target_feature
159 .split(',')
83c7162d 160 .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s)));
fc512014 161 sess.target.features.split(',').chain(cmdline).filter(|l| !l.is_empty())
83c7162d
XL
162}
163
b7449926 164pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
0731742a 165 let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess));
b7449926 166 llvm::AddFunctionAttrStringValue(
dfeec247
XL
167 llfn,
168 llvm::AttributePlace::Function,
169 const_cstr!("target-cpu"),
170 target_cpu.as_c_str(),
171 );
b7449926
XL
172}
173
29967ef6
XL
174pub fn apply_tune_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
175 if let Some(tune) = llvm_util::tune_cpu(cx.tcx.sess) {
176 let tune_cpu = SmallCStr::new(tune);
177 llvm::AddFunctionAttrStringValue(
178 llfn,
179 llvm::AttributePlace::Function,
180 const_cstr!("tune-cpu"),
181 tune_cpu.as_c_str(),
182 );
183 }
184}
185
0bf4aa26
XL
186/// Sets the `NonLazyBind` LLVM attribute on a given function,
187/// assuming the codegen options allow skipping the PLT.
188pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) {
189 // Don't generate calls through PLT if it's not necessary
190 if !sess.needs_plt() {
191 Attribute::NonLazyBind.apply_llfn(Function, llfn);
192 }
193}
194
9fa01778
XL
195pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) {
196 match sess.opts.optimize {
197 OptLevel::Size => {
198 llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
199 llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
200 llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
dfeec247 201 }
9fa01778
XL
202 OptLevel::SizeMin => {
203 llvm::Attribute::MinSize.apply_llfn(Function, llfn);
204 llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
205 llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
206 }
207 OptLevel::No => {
208 llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
209 llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
210 llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
211 }
212 _ => {}
213 }
214}
215
0731742a 216/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
3157f602 217/// attributes.
ba9703b0 218pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::Instance<'tcx>) {
60c5eb7d 219 let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
0531ce1d 220
9fa01778
XL
221 match codegen_fn_attrs.optimize {
222 OptimizeAttr::None => {
223 default_optimisation_attrs(cx.tcx.sess, llfn);
224 }
225 OptimizeAttr::Speed => {
226 llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
227 llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
228 llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
229 }
230 OptimizeAttr::Size => {
231 llvm::Attribute::MinSize.apply_llfn(Function, llfn);
232 llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
233 llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
234 }
235 }
236
fc512014
XL
237 let inline_attr = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
238 InlineAttr::Never
239 } else if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) {
240 InlineAttr::Hint
241 } else {
242 codegen_fn_attrs.inline
243 };
244 inline(cx, llfn, inline_attr);
b7449926
XL
245
246 // The `uwtable` attribute according to LLVM is:
247 //
248 // This attribute indicates that the ABI being targeted requires that an
249 // unwind table entry be produced for this function even if we can show
250 // that no exceptions passes by it. This is normally the case for the
251 // ELF x86-64 abi, but it can be disabled for some compilation units.
252 //
253 // Typically when we're compiling with `-C panic=abort` (which implies this
254 // `no_landing_pads` check) we don't need `uwtable` because we can't
255 // generate any exceptions! On Windows, however, exceptions include other
256 // events such as illegal instructions, segfaults, etc. This means that on
257 // Windows we end up still needing the `uwtable` attribute even if the `-C
258 // panic=abort` flag is passed.
259 //
f035d41b 260 // You can also find more info on why Windows always requires uwtables here:
b7449926 261 // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
f9f354fc 262 if cx.sess().must_emit_unwind_tables() {
b7449926
XL
263 attributes::emit_uwtable(llfn, true);
264 }
3157f602 265
2c00a5a8 266 set_frame_pointer_elimination(cx, llfn);
0731742a 267 set_instrument_function(cx, llfn);
2c00a5a8
XL
268 set_probestack(cx, llfn);
269
94b46f34 270 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
0531ce1d
XL
271 Attribute::Cold.apply_llfn(Function, llfn);
272 }
9fa01778
XL
273 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
274 Attribute::ReturnsTwice.apply_llfn(Function, llfn);
275 }
f9f354fc
XL
276 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
277 Attribute::ReadOnly.apply_llfn(Function, llfn);
278 }
279 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
280 Attribute::ReadNone.apply_llfn(Function, llfn);
281 }
94b46f34 282 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
0531ce1d
XL
283 naked(llfn, true);
284 }
94b46f34 285 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
dfeec247 286 Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
0531ce1d 287 }
1b1a35ee
XL
288 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) {
289 llvm::AddFunctionAttrString(llfn, Function, const_cstr!("cmse_nonsecure_entry"));
290 }
f035d41b 291 sanitize(cx, codegen_fn_attrs.no_sanitize, llfn);
94b46f34 292
b7449926
XL
293 // Always annotate functions with the target-cpu they are compiled for.
294 // Without this, ThinLTO won't inline Rust functions into Clang generated
295 // functions (because Clang annotates functions this way too).
0731742a 296 apply_target_cpu_attr(cx, llfn);
29967ef6
XL
297 // tune-cpu is only conveyed through the attribute for our purpose.
298 // The target doesn't care; the subtarget reads our attribute.
299 apply_tune_cpu_attr(cx, llfn);
b7449926 300
83c7162d
XL
301 let features = llvm_target_features(cx.tcx.sess)
302 .map(|s| s.to_string())
dfeec247
XL
303 .chain(codegen_fn_attrs.target_features.iter().map(|f| {
304 let feature = &f.as_str();
305 format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature))
306 }))
29967ef6
XL
307 .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
308 InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(),
309 InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(),
310 }))
0531ce1d
XL
311 .collect::<Vec<String>>()
312 .join(",");
313
314 if !features.is_empty() {
315 let val = CString::new(features).unwrap();
476ff2be 316 llvm::AddFunctionAttrStringValue(
dfeec247
XL
317 llfn,
318 llvm::AttributePlace::Function,
319 const_cstr!("target-features"),
320 &val,
321 );
476ff2be 322 }
0531ce1d
XL
323
324 // Note that currently the `wasm-import-module` doesn't do anything, but
325 // eventually LLVM 7 should read this and ferry the appropriate import
326 // module to the output file.
29967ef6 327 if cx.tcx.sess.target.arch == "wasm32" {
60c5eb7d
XL
328 if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
329 llvm::AddFunctionAttrStringValue(
330 llfn,
331 llvm::AttributePlace::Function,
332 const_cstr!("wasm-import-module"),
333 &module,
334 );
dfeec247
XL
335
336 let name =
337 codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
338 let name = CString::new(&name.as_str()[..]).unwrap();
339 llvm::AddFunctionAttrStringValue(
340 llfn,
341 llvm::AttributePlace::Function,
342 const_cstr!("wasm-import-name"),
343 &name,
344 );
0531ce1d
XL
345 }
346 }
476ff2be
SL
347}
348
29967ef6 349pub fn provide_both(providers: &mut Providers) {
0531ce1d 350 providers.wasm_import_module_map = |tcx, cnum| {
f9f354fc
XL
351 // Build up a map from DefId to a `NativeLib` structure, where
352 // `NativeLib` internally contains information about
8faf50e0
XL
353 // `#[link(wasm_import_module = "...")]` for example.
354 let native_libs = tcx.native_libraries(cnum);
0bf4aa26 355
dfeec247
XL
356 let def_id_to_native_lib = native_libs
357 .iter()
358 .filter_map(|lib| lib.foreign_module.map(|id| (id, lib)))
359 .collect::<FxHashMap<_, _>>();
8faf50e0 360
0bf4aa26 361 let mut ret = FxHashMap::default();
29967ef6
XL
362 for (def_id, lib) in tcx.foreign_modules(cnum).iter() {
363 let module = def_id_to_native_lib.get(&def_id).and_then(|s| s.wasm_import_module);
0531ce1d
XL
364 let module = match module {
365 Some(s) => s,
366 None => continue,
367 };
0bf4aa26 368 ret.extend(lib.foreign_items.iter().map(|id| {
0531ce1d 369 assert_eq!(id.krate, cnum);
0bf4aa26
XL
370 (*id, module.to_string())
371 }));
2c00a5a8 372 }
0531ce1d 373
f9f354fc 374 ret
8faf50e0 375 };
2c00a5a8 376}
0531ce1d 377
dc9dc135 378fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<CString> {
dfeec247 379 tcx.wasm_import_module_map(id.krate).get(&id).map(|s| CString::new(&s[..]).unwrap())
0531ce1d 380}