]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/attributes.rs
Rollup merge of #74136 - JohnTitor:index-page-link, r=GuillaumeGomez
[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_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     // Flag our internal `__rust_probestack` function as the stack probe symbol.
137     // This is defined in the `compiler-builtins` crate for each architecture.
138     llvm::AddFunctionAttrStringValue(
139         llfn,
140         llvm::AttributePlace::Function,
141         const_cstr!("probe-stack"),
142         const_cstr!("__rust_probestack"),
143     );
144 }
145
146 fn translate_obsolete_target_features(feature: &str) -> &str {
147     const LLVM9_FEATURE_CHANGES: &[(&str, &str)] =
148         &[("+fp-only-sp", "-fp64"), ("-fp-only-sp", "+fp64"), ("+d16", "-d32"), ("-d16", "+d32")];
149     if llvm_util::get_major_version() >= 9 {
150         for &(old, new) in LLVM9_FEATURE_CHANGES {
151             if feature == old {
152                 return new;
153             }
154         }
155     } else {
156         for &(old, new) in LLVM9_FEATURE_CHANGES {
157             if feature == new {
158                 return old;
159             }
160         }
161     }
162     feature
163 }
164
165 pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {
166     const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
167
168     let cmdline = sess
169         .opts
170         .cg
171         .target_feature
172         .split(',')
173         .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s)));
174     sess.target
175         .target
176         .options
177         .features
178         .split(',')
179         .chain(cmdline)
180         .filter(|l| !l.is_empty())
181         .map(translate_obsolete_target_features)
182 }
183
184 pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
185     let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess));
186     llvm::AddFunctionAttrStringValue(
187         llfn,
188         llvm::AttributePlace::Function,
189         const_cstr!("target-cpu"),
190         target_cpu.as_c_str(),
191     );
192 }
193
194 /// Sets the `NonLazyBind` LLVM attribute on a given function,
195 /// assuming the codegen options allow skipping the PLT.
196 pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) {
197     // Don't generate calls through PLT if it's not necessary
198     if !sess.needs_plt() {
199         Attribute::NonLazyBind.apply_llfn(Function, llfn);
200     }
201 }
202
203 pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) {
204     match sess.opts.optimize {
205         OptLevel::Size => {
206             llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
207             llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
208             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
209         }
210         OptLevel::SizeMin => {
211             llvm::Attribute::MinSize.apply_llfn(Function, llfn);
212             llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
213             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
214         }
215         OptLevel::No => {
216             llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
217             llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
218             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
219         }
220         _ => {}
221     }
222 }
223
224 /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
225 /// attributes.
226 pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::Instance<'tcx>) {
227     let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
228
229     match codegen_fn_attrs.optimize {
230         OptimizeAttr::None => {
231             default_optimisation_attrs(cx.tcx.sess, llfn);
232         }
233         OptimizeAttr::Speed => {
234             llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
235             llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
236             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
237         }
238         OptimizeAttr::Size => {
239             llvm::Attribute::MinSize.apply_llfn(Function, llfn);
240             llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
241             llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
242         }
243     }
244
245     // FIXME(eddyb) consolidate these two `inline` calls (and avoid overwrites).
246     if instance.def.requires_inline(cx.tcx) {
247         inline(cx, llfn, attributes::InlineAttr::Hint);
248     }
249
250     inline(cx, llfn, codegen_fn_attrs.inline.clone());
251
252     // The `uwtable` attribute according to LLVM is:
253     //
254     //     This attribute indicates that the ABI being targeted requires that an
255     //     unwind table entry be produced for this function even if we can show
256     //     that no exceptions passes by it. This is normally the case for the
257     //     ELF x86-64 abi, but it can be disabled for some compilation units.
258     //
259     // Typically when we're compiling with `-C panic=abort` (which implies this
260     // `no_landing_pads` check) we don't need `uwtable` because we can't
261     // generate any exceptions! On Windows, however, exceptions include other
262     // events such as illegal instructions, segfaults, etc. This means that on
263     // Windows we end up still needing the `uwtable` attribute even if the `-C
264     // panic=abort` flag is passed.
265     //
266     // You can also find more info on why Windows is whitelisted here in:
267     //      https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
268     if cx.sess().must_emit_unwind_tables() {
269         attributes::emit_uwtable(llfn, true);
270     }
271
272     set_frame_pointer_elimination(cx, llfn);
273     set_instrument_function(cx, llfn);
274     set_probestack(cx, llfn);
275
276     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
277         Attribute::Cold.apply_llfn(Function, llfn);
278     }
279     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
280         Attribute::ReturnsTwice.apply_llfn(Function, llfn);
281     }
282     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
283         Attribute::ReadOnly.apply_llfn(Function, llfn);
284     }
285     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
286         Attribute::ReadNone.apply_llfn(Function, llfn);
287     }
288     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
289         naked(llfn, true);
290     }
291     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
292         Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
293     }
294     sanitize(cx, codegen_fn_attrs.no_sanitize, llfn);
295
296     // Always annotate functions with the target-cpu they are compiled for.
297     // Without this, ThinLTO won't inline Rust functions into Clang generated
298     // functions (because Clang annotates functions this way too).
299     apply_target_cpu_attr(cx, llfn);
300
301     let features = llvm_target_features(cx.tcx.sess)
302         .map(|s| s.to_string())
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         }))
307         .collect::<Vec<String>>()
308         .join(",");
309
310     if !features.is_empty() {
311         let val = CString::new(features).unwrap();
312         llvm::AddFunctionAttrStringValue(
313             llfn,
314             llvm::AttributePlace::Function,
315             const_cstr!("target-features"),
316             &val,
317         );
318     }
319
320     // Note that currently the `wasm-import-module` doesn't do anything, but
321     // eventually LLVM 7 should read this and ferry the appropriate import
322     // module to the output file.
323     if cx.tcx.sess.target.target.arch == "wasm32" {
324         if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) {
325             llvm::AddFunctionAttrStringValue(
326                 llfn,
327                 llvm::AttributePlace::Function,
328                 const_cstr!("wasm-import-module"),
329                 &module,
330             );
331
332             let name =
333                 codegen_fn_attrs.link_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id()));
334             let name = CString::new(&name.as_str()[..]).unwrap();
335             llvm::AddFunctionAttrStringValue(
336                 llfn,
337                 llvm::AttributePlace::Function,
338                 const_cstr!("wasm-import-name"),
339                 &name,
340             );
341         }
342     }
343 }
344
345 pub fn provide(providers: &mut Providers) {
346     providers.target_features_whitelist = |tcx, cnum| {
347         assert_eq!(cnum, LOCAL_CRATE);
348         if tcx.sess.opts.actually_rustdoc {
349             // rustdoc needs to be able to document functions that use all the features, so
350             // whitelist them all
351             llvm_util::all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
352         } else {
353             llvm_util::target_feature_whitelist(tcx.sess)
354                 .iter()
355                 .map(|&(a, b)| (a.to_string(), b))
356                 .collect()
357         }
358     };
359
360     provide_extern(providers);
361 }
362
363 pub fn provide_extern(providers: &mut Providers) {
364     providers.wasm_import_module_map = |tcx, cnum| {
365         // Build up a map from DefId to a `NativeLib` structure, where
366         // `NativeLib` internally contains information about
367         // `#[link(wasm_import_module = "...")]` for example.
368         let native_libs = tcx.native_libraries(cnum);
369
370         let def_id_to_native_lib = native_libs
371             .iter()
372             .filter_map(|lib| lib.foreign_module.map(|id| (id, lib)))
373             .collect::<FxHashMap<_, _>>();
374
375         let mut ret = FxHashMap::default();
376         for lib in tcx.foreign_modules(cnum).iter() {
377             let module = def_id_to_native_lib.get(&lib.def_id).and_then(|s| s.wasm_import_module);
378             let module = match module {
379                 Some(s) => s,
380                 None => continue,
381             };
382             ret.extend(lib.foreign_items.iter().map(|id| {
383                 assert_eq!(id.krate, cnum);
384                 (*id, module.to_string())
385             }));
386         }
387
388         ret
389     };
390 }
391
392 fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<CString> {
393     tcx.wasm_import_module_map(id.krate).get(&id).map(|s| CString::new(&s[..]).unwrap())
394 }