]> git.proxmox.com Git - rustc.git/blame - src/librustc_codegen_llvm/attributes.rs
New upstream version 1.46.0~beta.2+dfsg1
[rustc.git] / src / librustc_codegen_llvm / 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
XL
8use rustc_data_structures::small_c_str::SmallCStr;
9use rustc_hir::def_id::{DefId, LOCAL_CRATE};
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;
041b39d2 16
9fa01778 17use crate::attributes;
9fa01778 18use crate::llvm::AttributePlace::Function;
dfeec247 19use crate::llvm::{self, Attribute};
9fa01778 20use crate::llvm_util;
ba9703b0 21pub use rustc_attr::{InlineAttr, OptimizeAttr};
b7449926 22
9fa01778
XL
23use crate::context::CodegenCx;
24use crate::value::Value;
54a0048b
SL
25
26/// Mark LLVM function to use provided inline heuristic.
27#[inline]
60c5eb7d 28fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) {
54a0048b
SL
29 use self::InlineAttr::*;
30 match inline {
dfeec247 31 Hint => Attribute::InlineHint.apply_llfn(Function, val),
5bcae85e 32 Always => Attribute::AlwaysInline.apply_llfn(Function, val),
dfeec247 33 Never => {
b7449926
XL
34 if cx.tcx().sess.target.target.arch != "amdgpu" {
35 Attribute::NoInline.apply_llfn(Function, val);
36 }
dfeec247
XL
37 }
38 None => {
476ff2be
SL
39 Attribute::InlineHint.unapply_llfn(Function, val);
40 Attribute::AlwaysInline.unapply_llfn(Function, val);
41 Attribute::NoInline.unapply_llfn(Function, val);
dfeec247 42 }
54a0048b
SL
43 };
44}
45
74b04a01
XL
46/// Apply LLVM sanitize attributes.
47#[inline]
f035d41b
XL
48pub 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);
74b04a01
XL
58 }
59}
60
54a0048b
SL
61/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
62#[inline]
b7449926 63pub fn emit_uwtable(val: &'ll Value, emit: bool) {
5bcae85e 64 Attribute::UWTable.toggle_llfn(Function, val, emit);
54a0048b
SL
65}
66
0731742a 67/// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
54a0048b 68#[inline]
60c5eb7d 69fn naked(val: &'ll Value, is_naked: bool) {
5bcae85e 70 Attribute::Naked.toggle_llfn(Function, val, is_naked);
54a0048b
SL
71}
72
b7449926 73pub fn set_frame_pointer_elimination(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
2c00a5a8 74 if cx.sess().must_not_eliminate_frame_pointers() {
ba9703b0
XL
75 llvm::AddFunctionAttrStringValue(
76 llfn,
77 llvm::AttributePlace::Function,
78 const_cstr!("frame-pointer"),
79 const_cstr!("all"),
80 );
54a0048b 81 }
3157f602
XL
82}
83
0731742a
XL
84/// Tell LLVM what instrument function to insert.
85#[inline]
60c5eb7d 86fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
0731742a
XL
87 if cx.sess().instrument_mcount() {
88 // Similar to `clang -pg` behavior. Handled by the
89 // `post-inline-ee-instrument` LLVM pass.
532ac7d7
XL
90
91 // The function name varies on platforms.
92 // See test/CodeGen/mcount.c in clang.
dfeec247
XL
93 let mcount_name =
94 CString::new(cx.sess().target.target.options.target_mcount.as_str().as_bytes())
95 .unwrap();
532ac7d7 96
0731742a 97 llvm::AddFunctionAttrStringValue(
dfeec247
XL
98 llfn,
99 llvm::AttributePlace::Function,
100 const_cstr!("instrument-function-entry-inlined"),
101 &mcount_name,
102 );
0731742a
XL
103 }
104}
105
60c5eb7d 106fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
041b39d2
XL
107 // Only use stack probes if the target specification indicates that we
108 // should be using stack probes
2c00a5a8 109 if !cx.sess().target.target.options.stack_probes {
dfeec247 110 return;
041b39d2
XL
111 }
112
113 // Currently stack probes seem somewhat incompatible with the address
e74abb32
XL
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.
f035d41b
XL
116 if cx
117 .sess()
118 .opts
119 .debugging_opts
120 .sanitizer
121 .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
122 {
123 return;
041b39d2
XL
124 }
125
dc9dc135
XL
126 // probestack doesn't play nice either with `-C profile-generate`.
127 if cx.sess().opts.cg.profile_generate.enabled() {
0531ce1d
XL
128 return;
129 }
130
8faf50e0
XL
131 // probestack doesn't play nice either with gcov profiling.
132 if cx.sess().opts.debugging_opts.profile {
133 return;
134 }
135
041b39d2
XL
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(
dfeec247
XL
139 llfn,
140 llvm::AttributePlace::Function,
141 const_cstr!("probe-stack"),
142 const_cstr!("__rust_probestack"),
143 );
041b39d2
XL
144}
145
416331ca 146fn translate_obsolete_target_features(feature: &str) -> &str {
dfeec247
XL
147 const LLVM9_FEATURE_CHANGES: &[(&str, &str)] =
148 &[("+fp-only-sp", "-fp64"), ("-fp-only-sp", "+fp64"), ("+d16", "-d32"), ("-d16", "+d32")];
416331ca
XL
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
83c7162d 165pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {
dfeec247 166 const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
83c7162d 167
dfeec247
XL
168 let cmdline = sess
169 .opts
170 .cg
171 .target_feature
172 .split(',')
83c7162d 173 .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s)));
dfeec247
XL
174 sess.target
175 .target
176 .options
177 .features
178 .split(',')
83c7162d
XL
179 .chain(cmdline)
180 .filter(|l| !l.is_empty())
416331ca 181 .map(translate_obsolete_target_features)
83c7162d
XL
182}
183
b7449926 184pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
0731742a 185 let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess));
b7449926 186 llvm::AddFunctionAttrStringValue(
dfeec247
XL
187 llfn,
188 llvm::AttributePlace::Function,
189 const_cstr!("target-cpu"),
190 target_cpu.as_c_str(),
191 );
b7449926
XL
192}
193
0bf4aa26
XL
194/// Sets the `NonLazyBind` LLVM attribute on a given function,
195/// assuming the codegen options allow skipping the PLT.
196pub 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
9fa01778
XL
203pub(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);
dfeec247 209 }
9fa01778
XL
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
0731742a 224/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
3157f602 225/// attributes.
ba9703b0 226pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::Instance<'tcx>) {
60c5eb7d 227 let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
0531ce1d 228
9fa01778
XL
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
60c5eb7d 245 // FIXME(eddyb) consolidate these two `inline` calls (and avoid overwrites).
dfeec247 246 if instance.def.requires_inline(cx.tcx) {
60c5eb7d
XL
247 inline(cx, llfn, attributes::InlineAttr::Hint);
248 }
249
f9f354fc 250 inline(cx, llfn, codegen_fn_attrs.inline.clone());
b7449926
XL
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 //
f035d41b 266 // You can also find more info on why Windows always requires uwtables here:
b7449926 267 // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
f9f354fc 268 if cx.sess().must_emit_unwind_tables() {
b7449926
XL
269 attributes::emit_uwtable(llfn, true);
270 }
3157f602 271
2c00a5a8 272 set_frame_pointer_elimination(cx, llfn);
0731742a 273 set_instrument_function(cx, llfn);
2c00a5a8
XL
274 set_probestack(cx, llfn);
275
94b46f34 276 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
0531ce1d
XL
277 Attribute::Cold.apply_llfn(Function, llfn);
278 }
9fa01778
XL
279 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
280 Attribute::ReturnsTwice.apply_llfn(Function, llfn);
281 }
f9f354fc
XL
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 }
94b46f34 288 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
0531ce1d
XL
289 naked(llfn, true);
290 }
94b46f34 291 if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
dfeec247 292 Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
0531ce1d 293 }
f035d41b 294 sanitize(cx, codegen_fn_attrs.no_sanitize, llfn);
94b46f34 295
b7449926
XL
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).
0731742a 299 apply_target_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 }))
0531ce1d
XL
307 .collect::<Vec<String>>()
308 .join(",");
309
310 if !features.is_empty() {
311 let val = CString::new(features).unwrap();
476ff2be 312 llvm::AddFunctionAttrStringValue(
dfeec247
XL
313 llfn,
314 llvm::AttributePlace::Function,
315 const_cstr!("target-features"),
316 &val,
317 );
476ff2be 318 }
0531ce1d
XL
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.
60c5eb7d
XL
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 );
dfeec247
XL
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 );
0531ce1d
XL
341 }
342 }
476ff2be
SL
343}
344
f035d41b
XL
345pub fn provide(providers: &mut Providers) {
346 providers.supported_target_features = |tcx, cnum| {
2c00a5a8 347 assert_eq!(cnum, LOCAL_CRATE);
0531ce1d
XL
348 if tcx.sess.opts.actually_rustdoc {
349 // rustdoc needs to be able to document functions that use all the features, so
f035d41b 350 // provide them all.
f9f354fc 351 llvm_util::all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
0531ce1d 352 } else {
f035d41b 353 llvm_util::supported_target_features(tcx.sess)
f9f354fc
XL
354 .iter()
355 .map(|&(a, b)| (a.to_string(), b))
356 .collect()
0531ce1d 357 }
2c00a5a8
XL
358 };
359
0531ce1d 360 provide_extern(providers);
2c00a5a8
XL
361}
362
f035d41b 363pub fn provide_extern(providers: &mut Providers) {
0531ce1d 364 providers.wasm_import_module_map = |tcx, cnum| {
f9f354fc
XL
365 // Build up a map from DefId to a `NativeLib` structure, where
366 // `NativeLib` internally contains information about
8faf50e0
XL
367 // `#[link(wasm_import_module = "...")]` for example.
368 let native_libs = tcx.native_libraries(cnum);
0bf4aa26 369
dfeec247
XL
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<_, _>>();
8faf50e0 374
0bf4aa26 375 let mut ret = FxHashMap::default();
0531ce1d 376 for lib in tcx.foreign_modules(cnum).iter() {
dfeec247 377 let module = def_id_to_native_lib.get(&lib.def_id).and_then(|s| s.wasm_import_module);
0531ce1d
XL
378 let module = match module {
379 Some(s) => s,
380 None => continue,
381 };
0bf4aa26 382 ret.extend(lib.foreign_items.iter().map(|id| {
0531ce1d 383 assert_eq!(id.krate, cnum);
0bf4aa26
XL
384 (*id, module.to_string())
385 }));
2c00a5a8 386 }
0531ce1d 387
f9f354fc 388 ret
8faf50e0 389 };
2c00a5a8 390}
0531ce1d 391
dc9dc135 392fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<CString> {
dfeec247 393 tcx.wasm_import_module_map(id.krate).get(&id).map(|s| CString::new(&s[..]).unwrap())
0531ce1d 394}