]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/attributes.rs
Rollup merge of #69792 - LenaWil:try_reserve_error/impl-error, r=sfackler
[rust.git] / src / librustc_codegen_llvm / attributes.rs
1 //! Set and unset common attributes on LLVM values.
2
3 use std::ffi::CString;
4
5 use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags;
6 use rustc::session::config::{OptLevel, Sanitizer};
7 use rustc::session::Session;
8 use rustc::ty::layout::HasTyCtxt;
9 use rustc::ty::query::Providers;
10 use rustc::ty::{self, Ty, TyCtxt};
11 use rustc_codegen_ssa::traits::*;
12 use rustc_data_structures::const_cstr;
13 use rustc_data_structures::fx::FxHashMap;
14 use rustc_data_structures::small_c_str::SmallCStr;
15 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
16 use rustc_target::abi::call::Conv;
17 use rustc_target::spec::PanicStrategy;
18
19 use crate::abi::FnAbi;
20 use crate::attributes;
21 use crate::llvm::AttributePlace::Function;
22 use crate::llvm::{self, Attribute};
23 use crate::llvm_util;
24 pub use rustc_attr::{self as attr, InlineAttr, OptimizeAttr};
25
26 use crate::context::CodegenCx;
27 use crate::value::Value;
28
29 /// Mark LLVM function to use provided inline heuristic.
30 #[inline]
31 fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
32     use self::InlineAttr::*;
33     match inline {
34         Hint => Attribute::InlineHint.apply_llfn(Function, val),
35         Always => Attribute::AlwaysInline.apply_llfn(Function, val),
36         Never => {
37             if cx.tcx().sess.target.target.arch != "amdgpu" {
38                 Attribute::NoInline.apply_llfn(Function, val);
39             }
40         }
41         None => {
42             Attribute::InlineHint.unapply_llfn(Function, val);
43             Attribute::AlwaysInline.unapply_llfn(Function, val);
44             Attribute::NoInline.unapply_llfn(Function, val);
45         }
46     };
47 }
48
49 /// Apply LLVM sanitize attributes.
50 #[inline]
51 pub fn sanitize(cx: &CodegenCx<'ll, '_>, codegen_fn_flags: CodegenFnAttrFlags, llfn: &'ll Value) {
52     if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer {
53         match *sanitizer {
54             Sanitizer::Address => {
55                 if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) {
56                     llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn);
57                 }
58             }
59             Sanitizer::Memory => {
60                 if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) {
61                     llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn);
62                 }
63             }
64             Sanitizer::Thread => {
65                 if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) {
66                     llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn);
67                 }
68             }
69             Sanitizer::Leak => {}
70         }
71     }
72 }
73
74 /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
75 #[inline]
76 pub fn emit_uwtable(val: &'ll Value, emit: bool) {
77     Attribute::UWTable.toggle_llfn(Function, val, emit);
78 }
79
80 /// Tell LLVM whether the function can or cannot unwind.
81 #[inline]
82 fn unwind(val: &'ll Value, can_unwind: bool) {
83     Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind);
84 }
85
86 /// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
87 #[inline]
88 fn naked(val: &'ll Value, is_naked: bool) {
89     Attribute::Naked.toggle_llfn(Function, val, is_naked);
90 }
91
92 pub fn set_frame_pointer_elimination(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
93     if cx.sess().must_not_eliminate_frame_pointers() {
94         if llvm_util::get_major_version() >= 8 {
95             llvm::AddFunctionAttrStringValue(
96                 llfn,
97                 llvm::AttributePlace::Function,
98                 const_cstr!("frame-pointer"),
99                 const_cstr!("all"),
100             );
101         } else {
102             llvm::AddFunctionAttrStringValue(
103                 llfn,
104                 llvm::AttributePlace::Function,
105                 const_cstr!("no-frame-pointer-elim"),
106                 const_cstr!("true"),
107             );
108         }
109     }
110 }
111
112 /// Tell LLVM what instrument function to insert.
113 #[inline]
114 fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
115     if cx.sess().instrument_mcount() {
116         // Similar to `clang -pg` behavior. Handled by the
117         // `post-inline-ee-instrument` LLVM pass.
118
119         // The function name varies on platforms.
120         // See test/CodeGen/mcount.c in clang.
121         let mcount_name =
122             CString::new(cx.sess().target.target.options.target_mcount.as_str().as_bytes())
123                 .unwrap();
124
125         llvm::AddFunctionAttrStringValue(
126             llfn,
127             llvm::AttributePlace::Function,
128             const_cstr!("instrument-function-entry-inlined"),
129             &mcount_name,
130         );
131     }
132 }
133
134 fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
135     // Only use stack probes if the target specification indicates that we
136     // should be using stack probes
137     if !cx.sess().target.target.options.stack_probes {
138         return;
139     }
140
141     // Currently stack probes seem somewhat incompatible with the address
142     // sanitizer and thread sanitizer. With asan we're already protected from
143     // stack overflow anyway so we don't really need stack probes regardless.
144     match cx.sess().opts.debugging_opts.sanitizer {
145         Some(Sanitizer::Address) | Some(Sanitizer::Thread) => return,
146         _ => {}
147     }
148
149     // probestack doesn't play nice either with `-C profile-generate`.
150     if cx.sess().opts.cg.profile_generate.enabled() {
151         return;
152     }
153
154     // probestack doesn't play nice either with gcov profiling.
155     if cx.sess().opts.debugging_opts.profile {
156         return;
157     }
158
159     // Flag our internal `__rust_probestack` function as the stack probe symbol.
160     // This is defined in the `compiler-builtins` crate for each architecture.
161     llvm::AddFunctionAttrStringValue(
162         llfn,
163         llvm::AttributePlace::Function,
164         const_cstr!("probe-stack"),
165         const_cstr!("__rust_probestack"),
166     );
167 }
168
169 fn translate_obsolete_target_features(feature: &str) -> &str {
170     const LLVM9_FEATURE_CHANGES: &[(&str, &str)] =
171         &[("+fp-only-sp", "-fp64"), ("-fp-only-sp", "+fp64"), ("+d16", "-d32"), ("-d16", "+d32")];
172     if llvm_util::get_major_version() >= 9 {
173         for &(old, new) in LLVM9_FEATURE_CHANGES {
174             if feature == old {
175                 return new;
176             }
177         }
178     } else {
179         for &(old, new) in LLVM9_FEATURE_CHANGES {
180             if feature == new {
181                 return old;
182             }
183         }
184     }
185     feature
186 }
187
188 pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {
189     const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
190
191     let cmdline = sess
192         .opts
193         .cg
194         .target_feature
195         .split(',')
196         .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s)));
197     sess.target
198         .target
199         .options
200         .features
201         .split(',')
202         .chain(cmdline)
203         .filter(|l| !l.is_empty())
204         .map(translate_obsolete_target_features)
205 }
206
207 pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
208     let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess));
209     llvm::AddFunctionAttrStringValue(
210         llfn,
211         llvm::AttributePlace::Function,
212         const_cstr!("target-cpu"),
213         target_cpu.as_c_str(),
214     );
215 }
216
217 /// Sets the `NonLazyBind` LLVM attribute on a given function,
218 /// assuming the codegen options allow skipping the PLT.
219 pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) {
220     // Don't generate calls through PLT if it's not necessary
221     if !sess.needs_plt() {
222         Attribute::NonLazyBind.apply_llfn(Function, llfn);
223     }
224 }
225
226 pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) {
227     match sess.opts.optimize {
228         OptLevel::Size => {
229             llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
230             llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
231             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
232         }
233         OptLevel::SizeMin => {
234             llvm::Attribute::MinSize.apply_llfn(Function, llfn);
235             llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
236             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
237         }
238         OptLevel::No => {
239             llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
240             llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
241             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
242         }
243         _ => {}
244     }
245 }
246
247 /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
248 /// attributes.
249 pub fn from_fn_attrs(
250     cx: &CodegenCx<'ll, 'tcx>,
251     llfn: &'ll Value,
252     instance: ty::Instance<'tcx>,
253     fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
254 ) {
255     let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
256
257     match codegen_fn_attrs.optimize {
258         OptimizeAttr::None => {
259             default_optimisation_attrs(cx.tcx.sess, llfn);
260         }
261         OptimizeAttr::Speed => {
262             llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
263             llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
264             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
265         }
266         OptimizeAttr::Size => {
267             llvm::Attribute::MinSize.apply_llfn(Function, llfn);
268             llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
269             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
270         }
271     }
272
273     // FIXME(eddyb) consolidate these two `inline` calls (and avoid overwrites).
274     if instance.def.requires_inline(cx.tcx) {
275         inline(cx, llfn, attributes::InlineAttr::Hint);
276     }
277
278     inline(cx, llfn, codegen_fn_attrs.inline);
279
280     // The `uwtable` attribute according to LLVM is:
281     //
282     //     This attribute indicates that the ABI being targeted requires that an
283     //     unwind table entry be produced for this function even if we can show
284     //     that no exceptions passes by it. This is normally the case for the
285     //     ELF x86-64 abi, but it can be disabled for some compilation units.
286     //
287     // Typically when we're compiling with `-C panic=abort` (which implies this
288     // `no_landing_pads` check) we don't need `uwtable` because we can't
289     // generate any exceptions! On Windows, however, exceptions include other
290     // events such as illegal instructions, segfaults, etc. This means that on
291     // Windows we end up still needing the `uwtable` attribute even if the `-C
292     // panic=abort` flag is passed.
293     //
294     // You can also find more info on why Windows is whitelisted here in:
295     //      https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
296     if !cx.sess().no_landing_pads() || cx.sess().target.target.options.requires_uwtable {
297         attributes::emit_uwtable(llfn, true);
298     }
299
300     set_frame_pointer_elimination(cx, llfn);
301     set_instrument_function(cx, llfn);
302     set_probestack(cx, llfn);
303
304     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
305         Attribute::Cold.apply_llfn(Function, llfn);
306     }
307     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
308         Attribute::ReturnsTwice.apply_llfn(Function, llfn);
309     }
310     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
311         naked(llfn, true);
312     }
313     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
314         Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
315     }
316     sanitize(cx, codegen_fn_attrs.flags, llfn);
317
318     unwind(
319         llfn,
320         if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind {
321             // In panic=abort mode we assume nothing can unwind anywhere, so
322             // optimize based on this!
323             false
324         } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) {
325             // If a specific #[unwind] attribute is present, use that.
326             true
327         } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) {
328             // Special attribute for allocator functions, which can't unwind.
329             false
330         } else {
331             if fn_abi.conv == Conv::Rust {
332                 // Any Rust method (or `extern "Rust" fn` or `extern
333                 // "rust-call" fn`) is explicitly allowed to unwind
334                 // (unless it has no-unwind attribute, handled above).
335                 true
336             } else {
337                 // Anything else is either:
338                 //
339                 //  1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or
340                 //
341                 //  2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
342                 //
343                 // Foreign items (case 1) are assumed to not unwind; it is
344                 // UB otherwise. (At least for now; see also
345                 // rust-lang/rust#63909 and Rust RFC 2753.)
346                 //
347                 // Items defined in Rust with non-Rust ABIs (case 2) are also
348                 // not supposed to unwind. Whether this should be enforced
349                 // (versus stating it is UB) and *how* it would be enforced
350                 // is currently under discussion; see rust-lang/rust#58794.
351                 //
352                 // In either case, we mark item as explicitly nounwind.
353                 false
354             }
355         },
356     );
357
358     // Always annotate functions with the target-cpu they are compiled for.
359     // Without this, ThinLTO won't inline Rust functions into Clang generated
360     // functions (because Clang annotates functions this way too).
361     apply_target_cpu_attr(cx, llfn);
362
363     let features = llvm_target_features(cx.tcx.sess)
364         .map(|s| s.to_string())
365         .chain(codegen_fn_attrs.target_features.iter().map(|f| {
366             let feature = &f.as_str();
367             format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature))
368         }))
369         .collect::<Vec<String>>()
370         .join(",");
371
372     if !features.is_empty() {
373         let val = CString::new(features).unwrap();
374         llvm::AddFunctionAttrStringValue(
375             llfn,
376             llvm::AttributePlace::Function,
377             const_cstr!("target-features"),
378             &val,
379         );
380     }
381
382     // Note that currently the `wasm-import-module` doesn't do anything, but
383     // eventually LLVM 7 should read this and ferry the appropriate import
384     // module to the output file.
385     if cx.tcx.sess.target.target.arch == "wasm32" {
386         if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
387             llvm::AddFunctionAttrStringValue(
388                 llfn,
389                 llvm::AttributePlace::Function,
390                 const_cstr!("wasm-import-module"),
391                 &module,
392             );
393
394             let name =
395                 codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
396             let name = CString::new(&name.as_str()[..]).unwrap();
397             llvm::AddFunctionAttrStringValue(
398                 llfn,
399                 llvm::AttributePlace::Function,
400                 const_cstr!("wasm-import-name"),
401                 &name,
402             );
403         }
404     }
405 }
406
407 pub fn provide(providers: &mut Providers<'_>) {
408     providers.target_features_whitelist = |tcx, cnum| {
409         assert_eq!(cnum, LOCAL_CRATE);
410         if tcx.sess.opts.actually_rustdoc {
411             // rustdoc needs to be able to document functions that use all the features, so
412             // whitelist them all
413             tcx.arena
414                 .alloc(llvm_util::all_known_features().map(|(a, b)| (a.to_string(), b)).collect())
415         } else {
416             tcx.arena.alloc(
417                 llvm_util::target_feature_whitelist(tcx.sess)
418                     .iter()
419                     .map(|&(a, b)| (a.to_string(), b))
420                     .collect(),
421             )
422         }
423     };
424
425     provide_extern(providers);
426 }
427
428 pub fn provide_extern(providers: &mut Providers<'_>) {
429     providers.wasm_import_module_map = |tcx, cnum| {
430         // Build up a map from DefId to a `NativeLibrary` structure, where
431         // `NativeLibrary` internally contains information about
432         // `#[link(wasm_import_module = "...")]` for example.
433         let native_libs = tcx.native_libraries(cnum);
434
435         let def_id_to_native_lib = native_libs
436             .iter()
437             .filter_map(|lib| lib.foreign_module.map(|id| (id, lib)))
438             .collect::<FxHashMap<_, _>>();
439
440         let mut ret = FxHashMap::default();
441         for lib in tcx.foreign_modules(cnum).iter() {
442             let module = def_id_to_native_lib.get(&lib.def_id).and_then(|s| s.wasm_import_module);
443             let module = match module {
444                 Some(s) => s,
445                 None => continue,
446             };
447             ret.extend(lib.foreign_items.iter().map(|id| {
448                 assert_eq!(id.krate, cnum);
449                 (*id, module.to_string())
450             }));
451         }
452
453         tcx.arena.alloc(ret)
454     };
455 }
456
457 fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<CString> {
458     tcx.wasm_import_module_map(id.krate).get(&id).map(|s| CString::new(&s[..]).unwrap())
459 }