]>
Commit | Line | Data |
---|---|---|
54a0048b SL |
1 | //! Set and unset common attributes on LLVM values. |
2 | ||
b7449926 | 3 | use std::ffi::CString; |
476ff2be | 4 | |
dfeec247 XL |
5 | use rustc_codegen_ssa::traits::*; |
6 | use rustc_data_structures::const_cstr; | |
0531ce1d | 7 | use rustc_data_structures::fx::FxHashMap; |
dfeec247 XL |
8 | use rustc_data_structures::small_c_str::SmallCStr; |
9 | use rustc_hir::def_id::{DefId, LOCAL_CRATE}; | |
ba9703b0 XL |
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}; | |
f035d41b | 14 | use rustc_session::config::{OptLevel, SanitizerSet}; |
ba9703b0 | 15 | use rustc_session::Session; |
041b39d2 | 16 | |
9fa01778 | 17 | use crate::attributes; |
9fa01778 | 18 | use crate::llvm::AttributePlace::Function; |
dfeec247 | 19 | use crate::llvm::{self, Attribute}; |
9fa01778 | 20 | use crate::llvm_util; |
ba9703b0 | 21 | pub use rustc_attr::{InlineAttr, OptimizeAttr}; |
b7449926 | 22 | |
9fa01778 XL |
23 | use crate::context::CodegenCx; |
24 | use crate::value::Value; | |
54a0048b SL |
25 | |
26 | /// Mark LLVM function to use provided inline heuristic. | |
27 | #[inline] | |
60c5eb7d | 28 | fn 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 |
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); | |
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 | 63 | pub 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 | 69 | fn naked(val: &'ll Value, is_naked: bool) { |
5bcae85e | 70 | Attribute::Naked.toggle_llfn(Function, val, is_naked); |
54a0048b SL |
71 | } |
72 | ||
b7449926 | 73 | pub 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 | 86 | fn 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 | 106 | fn 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 | 146 | fn 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 | 165 | pub 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 | 184 | pub 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. | |
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 | ||
9fa01778 XL |
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); | |
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 | 226 | pub 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 |
345 | pub 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 | 363 | pub 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 | 392 | fn 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 | } |