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