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