1 //! Set and unset common attributes on LLVM values.
5 use rustc
::hir
::{CodegenFnAttrFlags, CodegenFnAttrs}
;
6 use rustc
::hir
::def_id
::{DefId, LOCAL_CRATE}
;
7 use rustc
::session
::Session
;
8 use rustc
::session
::config
::{Sanitizer, OptLevel}
;
9 use rustc
::ty
::{self, TyCtxt, PolyFnSig}
;
10 use rustc
::ty
::layout
::HasTyCtxt
;
11 use rustc
::ty
::query
::Providers
;
12 use rustc_data_structures
::small_c_str
::SmallCStr
;
13 use rustc_data_structures
::sync
::Lrc
;
14 use rustc_data_structures
::fx
::FxHashMap
;
15 use rustc_target
::spec
::PanicStrategy
;
16 use rustc_codegen_ssa
::traits
::*;
19 use crate::attributes
;
20 use crate::llvm
::{self, Attribute}
;
21 use crate::llvm
::AttributePlace
::Function
;
23 pub use syntax
::attr
::{self, InlineAttr, OptimizeAttr}
;
25 use crate::context
::CodegenCx
;
26 use crate::value
::Value
;
28 /// Mark LLVM function to use provided inline heuristic.
30 pub fn inline(cx
: &CodegenCx
<'ll
, '_
>, val
: &'ll Value
, inline
: InlineAttr
) {
31 use self::InlineAttr
::*;
33 Hint
=> Attribute
::InlineHint
.apply_llfn(Function
, val
),
34 Always
=> Attribute
::AlwaysInline
.apply_llfn(Function
, val
),
36 if cx
.tcx().sess
.target
.target
.arch
!= "amdgpu" {
37 Attribute
::NoInline
.apply_llfn(Function
, val
);
41 Attribute
::InlineHint
.unapply_llfn(Function
, val
);
42 Attribute
::AlwaysInline
.unapply_llfn(Function
, val
);
43 Attribute
::NoInline
.unapply_llfn(Function
, val
);
48 /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
50 pub fn emit_uwtable(val
: &'ll Value
, emit
: bool
) {
51 Attribute
::UWTable
.toggle_llfn(Function
, val
, emit
);
54 /// Tell LLVM whether the function can or cannot unwind.
56 fn unwind(val
: &'ll Value
, can_unwind
: bool
) {
57 Attribute
::NoUnwind
.toggle_llfn(Function
, val
, !can_unwind
);
60 /// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
62 pub fn naked(val
: &'ll Value
, is_naked
: bool
) {
63 Attribute
::Naked
.toggle_llfn(Function
, val
, is_naked
);
66 pub fn set_frame_pointer_elimination(cx
: &CodegenCx
<'ll
, '_
>, llfn
: &'ll Value
) {
67 if cx
.sess().must_not_eliminate_frame_pointers() {
68 llvm
::AddFunctionAttrStringValue(
69 llfn
, llvm
::AttributePlace
::Function
,
70 const_cstr
!("no-frame-pointer-elim"), const_cstr
!("true"));
74 /// Tell LLVM what instrument function to insert.
76 pub fn set_instrument_function(cx
: &CodegenCx
<'ll
, '_
>, llfn
: &'ll Value
) {
77 if cx
.sess().instrument_mcount() {
78 // Similar to `clang -pg` behavior. Handled by the
79 // `post-inline-ee-instrument` LLVM pass.
80 llvm
::AddFunctionAttrStringValue(
81 llfn
, llvm
::AttributePlace
::Function
,
82 const_cstr
!("instrument-function-entry-inlined"), const_cstr
!("mcount"));
86 pub fn set_probestack(cx
: &CodegenCx
<'ll
, '_
>, llfn
: &'ll Value
) {
87 // Only use stack probes if the target specification indicates that we
88 // should be using stack probes
89 if !cx
.sess().target
.target
.options
.stack_probes
{
93 // Currently stack probes seem somewhat incompatible with the address
94 // sanitizer. With asan we're already protected from stack overflow anyway
95 // so we don't really need stack probes regardless.
96 if let Some(Sanitizer
::Address
) = cx
.sess().opts
.debugging_opts
.sanitizer
{
100 // probestack doesn't play nice either with pgo-gen.
101 if cx
.sess().opts
.debugging_opts
.pgo_gen
.is_some() {
105 // probestack doesn't play nice either with gcov profiling.
106 if cx
.sess().opts
.debugging_opts
.profile
{
110 // Flag our internal `__rust_probestack` function as the stack probe symbol.
111 // This is defined in the `compiler-builtins` crate for each architecture.
112 llvm
::AddFunctionAttrStringValue(
113 llfn
, llvm
::AttributePlace
::Function
,
114 const_cstr
!("probe-stack"), const_cstr
!("__rust_probestack"));
117 pub fn llvm_target_features(sess
: &Session
) -> impl Iterator
<Item
= &str> {
118 const RUSTC_SPECIFIC_FEATURES
: &[&str] = &[
122 let cmdline
= sess
.opts
.cg
.target_feature
.split('
,'
)
123 .filter(|f
| !RUSTC_SPECIFIC_FEATURES
.iter().any(|s
| f
.contains(s
)));
124 sess
.target
.target
.options
.features
.split('
,'
)
126 .filter(|l
| !l
.is_empty())
129 pub fn apply_target_cpu_attr(cx
: &CodegenCx
<'ll
, '_
>, llfn
: &'ll Value
) {
130 let target_cpu
= SmallCStr
::new(llvm_util
::target_cpu(cx
.tcx
.sess
));
131 llvm
::AddFunctionAttrStringValue(
133 llvm
::AttributePlace
::Function
,
134 const_cstr
!("target-cpu"),
135 target_cpu
.as_c_str());
138 /// Sets the `NonLazyBind` LLVM attribute on a given function,
139 /// assuming the codegen options allow skipping the PLT.
140 pub fn non_lazy_bind(sess
: &Session
, llfn
: &'ll Value
) {
141 // Don't generate calls through PLT if it's not necessary
142 if !sess
.needs_plt() {
143 Attribute
::NonLazyBind
.apply_llfn(Function
, llfn
);
147 pub(crate) fn default_optimisation_attrs(sess
: &Session
, llfn
: &'ll Value
) {
148 match sess
.opts
.optimize
{
150 llvm
::Attribute
::MinSize
.unapply_llfn(Function
, llfn
);
151 llvm
::Attribute
::OptimizeForSize
.apply_llfn(Function
, llfn
);
152 llvm
::Attribute
::OptimizeNone
.unapply_llfn(Function
, llfn
);
154 OptLevel
::SizeMin
=> {
155 llvm
::Attribute
::MinSize
.apply_llfn(Function
, llfn
);
156 llvm
::Attribute
::OptimizeForSize
.apply_llfn(Function
, llfn
);
157 llvm
::Attribute
::OptimizeNone
.unapply_llfn(Function
, llfn
);
160 llvm
::Attribute
::MinSize
.unapply_llfn(Function
, llfn
);
161 llvm
::Attribute
::OptimizeForSize
.unapply_llfn(Function
, llfn
);
162 llvm
::Attribute
::OptimizeNone
.unapply_llfn(Function
, llfn
);
169 /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
171 pub fn from_fn_attrs(
172 cx
: &CodegenCx
<'ll
, 'tcx
>,
175 sig
: PolyFnSig
<'tcx
>,
177 let codegen_fn_attrs
= id
.map(|id
| cx
.tcx
.codegen_fn_attrs(id
))
178 .unwrap_or_else(|| CodegenFnAttrs
::new());
180 match codegen_fn_attrs
.optimize
{
181 OptimizeAttr
::None
=> {
182 default_optimisation_attrs(cx
.tcx
.sess
, llfn
);
184 OptimizeAttr
::Speed
=> {
185 llvm
::Attribute
::MinSize
.unapply_llfn(Function
, llfn
);
186 llvm
::Attribute
::OptimizeForSize
.unapply_llfn(Function
, llfn
);
187 llvm
::Attribute
::OptimizeNone
.unapply_llfn(Function
, llfn
);
189 OptimizeAttr
::Size
=> {
190 llvm
::Attribute
::MinSize
.apply_llfn(Function
, llfn
);
191 llvm
::Attribute
::OptimizeForSize
.apply_llfn(Function
, llfn
);
192 llvm
::Attribute
::OptimizeNone
.unapply_llfn(Function
, llfn
);
196 inline(cx
, llfn
, codegen_fn_attrs
.inline
);
198 // The `uwtable` attribute according to LLVM is:
200 // This attribute indicates that the ABI being targeted requires that an
201 // unwind table entry be produced for this function even if we can show
202 // that no exceptions passes by it. This is normally the case for the
203 // ELF x86-64 abi, but it can be disabled for some compilation units.
205 // Typically when we're compiling with `-C panic=abort` (which implies this
206 // `no_landing_pads` check) we don't need `uwtable` because we can't
207 // generate any exceptions! On Windows, however, exceptions include other
208 // events such as illegal instructions, segfaults, etc. This means that on
209 // Windows we end up still needing the `uwtable` attribute even if the `-C
210 // panic=abort` flag is passed.
212 // You can also find more info on why Windows is whitelisted here in:
213 // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
214 if !cx
.sess().no_landing_pads() ||
215 cx
.sess().target
.target
.options
.requires_uwtable
{
216 attributes
::emit_uwtable(llfn
, true);
219 set_frame_pointer_elimination(cx
, llfn
);
220 set_instrument_function(cx
, llfn
);
221 set_probestack(cx
, llfn
);
223 if codegen_fn_attrs
.flags
.contains(CodegenFnAttrFlags
::COLD
) {
224 Attribute
::Cold
.apply_llfn(Function
, llfn
);
226 if codegen_fn_attrs
.flags
.contains(CodegenFnAttrFlags
::FFI_RETURNS_TWICE
) {
227 Attribute
::ReturnsTwice
.apply_llfn(Function
, llfn
);
229 if codegen_fn_attrs
.flags
.contains(CodegenFnAttrFlags
::NAKED
) {
232 if codegen_fn_attrs
.flags
.contains(CodegenFnAttrFlags
::ALLOCATOR
) {
233 Attribute
::NoAlias
.apply_llfn(
234 llvm
::AttributePlace
::ReturnValue
, llfn
);
237 unwind(llfn
, if cx
.tcx
.sess
.panic_strategy() != PanicStrategy
::Unwind
{
238 // In panic=abort mode we assume nothing can unwind anywhere, so
239 // optimize based on this!
241 } else if codegen_fn_attrs
.flags
.contains(CodegenFnAttrFlags
::UNWIND
) {
242 // If a specific #[unwind] attribute is present, use that
244 } else if codegen_fn_attrs
.flags
.contains(CodegenFnAttrFlags
::RUSTC_ALLOCATOR_NOUNWIND
) {
245 // Special attribute for allocator functions, which can't unwind
247 } else if let Some(id
) = id
{
248 let sig
= cx
.tcx
.normalize_erasing_late_bound_regions(ty
::ParamEnv
::reveal_all(), &sig
);
249 if cx
.tcx
.is_foreign_item(id
) {
250 // Foreign items like `extern "C" { fn foo(); }` are assumed not to
253 } else if sig
.abi
!= Abi
::Rust
&& sig
.abi
!= Abi
::RustCall
{
254 // Any items defined in Rust that *don't* have the `extern` ABI are
255 // defined to not unwind. We insert shims to abort if an unwind
256 // happens to enforce this.
259 // Anything else defined in Rust is assumed that it can possibly
264 // assume this can possibly unwind, avoiding the application of a
265 // `nounwind` attribute below.
269 // Always annotate functions with the target-cpu they are compiled for.
270 // Without this, ThinLTO won't inline Rust functions into Clang generated
271 // functions (because Clang annotates functions this way too).
272 apply_target_cpu_attr(cx
, llfn
);
274 let features
= llvm_target_features(cx
.tcx
.sess
)
275 .map(|s
| s
.to_string())
277 codegen_fn_attrs
.target_features
280 let feature
= &*f
.as_str();
281 format
!("+{}", llvm_util
::to_llvm_feature(cx
.tcx
.sess
, feature
))
284 .collect
::<Vec
<String
>>()
287 if !features
.is_empty() {
288 let val
= CString
::new(features
).unwrap();
289 llvm
::AddFunctionAttrStringValue(
290 llfn
, llvm
::AttributePlace
::Function
,
291 const_cstr
!("target-features"), &val
);
294 // Note that currently the `wasm-import-module` doesn't do anything, but
295 // eventually LLVM 7 should read this and ferry the appropriate import
296 // module to the output file.
297 if let Some(id
) = id
{
298 if cx
.tcx
.sess
.target
.target
.arch
== "wasm32" {
299 if let Some(module
) = wasm_import_module(cx
.tcx
, id
) {
300 llvm
::AddFunctionAttrStringValue(
302 llvm
::AttributePlace
::Function
,
303 const_cstr
!("wasm-import-module"),
311 pub fn provide(providers
: &mut Providers
<'_
>) {
312 providers
.target_features_whitelist
= |tcx
, cnum
| {
313 assert_eq
!(cnum
, LOCAL_CRATE
);
314 if tcx
.sess
.opts
.actually_rustdoc
{
315 // rustdoc needs to be able to document functions that use all the features, so
316 // whitelist them all
317 Lrc
::new(llvm_util
::all_known_features()
318 .map(|(a
, b
)| (a
.to_string(), b
.map(|s
| s
.to_string())))
321 Lrc
::new(llvm_util
::target_feature_whitelist(tcx
.sess
)
323 .map(|&(a
, b
)| (a
.to_string(), b
.map(|s
| s
.to_string())))
328 provide_extern(providers
);
331 pub fn provide_extern(providers
: &mut Providers
<'_
>) {
332 providers
.wasm_import_module_map
= |tcx
, cnum
| {
333 // Build up a map from DefId to a `NativeLibrary` structure, where
334 // `NativeLibrary` internally contains information about
335 // `#[link(wasm_import_module = "...")]` for example.
336 let native_libs
= tcx
.native_libraries(cnum
);
338 let def_id_to_native_lib
= native_libs
.iter().filter_map(|lib
|
339 if let Some(id
) = lib
.foreign_module
{
344 ).collect
::<FxHashMap
<_
, _
>>();
346 let mut ret
= FxHashMap
::default();
347 for lib
in tcx
.foreign_modules(cnum
).iter() {
348 let module
= def_id_to_native_lib
350 .and_then(|s
| s
.wasm_import_module
);
351 let module
= match module
{
355 ret
.extend(lib
.foreign_items
.iter().map(|id
| {
356 assert_eq
!(id
.krate
, cnum
);
357 (*id
, module
.to_string())
365 fn wasm_import_module(tcx
: TyCtxt
<'_
, '_
, '_
>, id
: DefId
) -> Option
<CString
> {
366 tcx
.wasm_import_module_map(id
.krate
)
368 .map(|s
| CString
::new(&s
[..]).unwrap())