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