attributes.rs 9.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Set and unset common attributes on LLVM values.

12 13
use std::ffi::{CStr, CString};

14
use rustc::hir::CodegenFnAttrFlags;
15
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
16
use rustc::session::Session;
17
use rustc::session::config::Sanitizer;
18
use rustc::ty::TyCtxt;
19
use rustc::ty::query::Providers;
20
use rustc_data_structures::sync::Lrc;
21
use rustc_data_structures::fx::FxHashMap;
22
use rustc_target::spec::PanicStrategy;
23

24
use attributes;
A
Ariel Ben-Yehuda 已提交
25 26
use llvm::{self, Attribute, ValueRef};
use llvm::AttributePlace::Function;
27
use llvm_util;
28
pub use syntax::attr::{self, InlineAttr};
29
use context::CodegenCx;
30 31 32 33

/// Mark LLVM function to use provided inline heuristic.
#[inline]
pub fn inline(val: ValueRef, inline: InlineAttr) {
34
    use self::InlineAttr::*;
35
    match inline {
A
Ariel Ben-Yehuda 已提交
36 37 38
        Hint   => Attribute::InlineHint.apply_llfn(Function, val),
        Always => Attribute::AlwaysInline.apply_llfn(Function, val),
        Never  => Attribute::NoInline.apply_llfn(Function, val),
39
        None   => {
40 41 42
            Attribute::InlineHint.unapply_llfn(Function, val);
            Attribute::AlwaysInline.unapply_llfn(Function, val);
            Attribute::NoInline.unapply_llfn(Function, val);
43 44 45 46 47 48 49
        },
    };
}

/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
#[inline]
pub fn emit_uwtable(val: ValueRef, emit: bool) {
A
Ariel Ben-Yehuda 已提交
50
    Attribute::UWTable.toggle_llfn(Function, val, emit);
51 52 53 54 55
}

/// Tell LLVM whether the function can or cannot unwind.
#[inline]
pub fn unwind(val: ValueRef, can_unwind: bool) {
A
Ariel Ben-Yehuda 已提交
56
    Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind);
57 58
}

F
Fourchaux 已提交
59
/// Tell LLVM whether it should optimize function for size.
60 61 62
#[inline]
#[allow(dead_code)] // possibly useful function
pub fn set_optimize_for_size(val: ValueRef, optimize: bool) {
A
Ariel Ben-Yehuda 已提交
63
    Attribute::OptimizeForSize.toggle_llfn(Function, val, optimize);
64 65
}

T
Ticki 已提交
66 67 68
/// Tell LLVM if this function should be 'naked', i.e. skip the epilogue and prologue.
#[inline]
pub fn naked(val: ValueRef, is_naked: bool) {
A
Ariel Ben-Yehuda 已提交
69
    Attribute::Naked.toggle_llfn(Function, val, is_naked);
T
Ticki 已提交
70 71
}

72 73
pub fn set_frame_pointer_elimination(cx: &CodegenCx, llfn: ValueRef) {
    if cx.sess().must_not_eliminate_frame_pointers() {
A
Ariel Ben-Yehuda 已提交
74
        llvm::AddFunctionAttrStringValue(
75 76
            llfn, llvm::AttributePlace::Function,
            cstr("no-frame-pointer-elim\0"), cstr("true\0"));
A
Alex Crichton 已提交
77
    }
78 79
}

80
pub fn set_probestack(cx: &CodegenCx, llfn: ValueRef) {
81 82
    // Only use stack probes if the target specification indicates that we
    // should be using stack probes
83
    if !cx.sess().target.target.options.stack_probes {
84 85 86 87 88 89
        return
    }

    // Currently stack probes seem somewhat incompatible with the address
    // sanitizer. With asan we're already protected from stack overflow anyway
    // so we don't really need stack probes regardless.
90
    match cx.sess().opts.debugging_opts.sanitizer {
91 92 93 94
        Some(Sanitizer::Address) => return,
        _ => {}
    }

95
    // probestack doesn't play nice either with pgo-gen.
96
    if cx.sess().opts.debugging_opts.pgo_gen.is_some() {
97 98 99
        return;
    }

100 101 102 103 104
    // probestack doesn't play nice either with gcov profiling.
    if cx.sess().opts.debugging_opts.profile {
        return;
    }

105 106 107 108 109 110 111
    // Flag our internal `__rust_probestack` function as the stack probe symbol.
    // This is defined in the `compiler-builtins` crate for each architecture.
    llvm::AddFunctionAttrStringValue(
        llfn, llvm::AttributePlace::Function,
        cstr("probe-stack\0"), cstr("__rust_probestack\0"));
}

112 113 114 115 116 117 118 119 120 121 122 123
pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {
    const RUSTC_SPECIFIC_FEATURES: &[&str] = &[
        "crt-static",
    ];

    let cmdline = sess.opts.cg.target_feature.split(',')
        .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s)));
    sess.target.target.options.features.split(',')
        .chain(cmdline)
        .filter(|l| !l.is_empty())
}

124 125
/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
/// attributes.
126
pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) {
I
Irina Popa 已提交
127
    let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(id);
W
Wesley Wiser 已提交
128

I
Irina Popa 已提交
129
    inline(llfn, codegen_fn_attrs.inline);
130

131 132
    set_frame_pointer_elimination(cx, llfn);
    set_probestack(cx, llfn);
133

I
Irina Popa 已提交
134
    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
W
Wesley Wiser 已提交
135 136
        Attribute::Cold.apply_llfn(Function, llfn);
    }
I
Irina Popa 已提交
137
    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
W
Wesley Wiser 已提交
138 139
        naked(llfn, true);
    }
I
Irina Popa 已提交
140
    if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
W
Wesley Wiser 已提交
141 142 143
        Attribute::NoAlias.apply_llfn(
            llvm::AttributePlace::ReturnValue, llfn);
    }
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

    let can_unwind = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) {
        Some(true)
    } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) {
        Some(false)

    // Perhaps questionable, but we assume that anything defined
    // *in Rust code* may unwind. Foreign items like `extern "C" {
    // fn foo(); }` are assumed not to unwind **unless** they have
    // a `#[unwind]` attribute.
    } else if !cx.tcx.is_foreign_item(id) {
        Some(true)
    } else {
        None
    };

    match can_unwind {
        Some(false) => attributes::unwind(llfn, false),
        Some(true) if cx.tcx.sess.panic_strategy() == PanicStrategy::Unwind => {
            attributes::unwind(llfn, true);
        }
        Some(true) | None => {}
166
    }
167

168 169 170
    let features = llvm_target_features(cx.tcx.sess)
        .map(|s| s.to_string())
        .chain(
I
Irina Popa 已提交
171
            codegen_fn_attrs.target_features
172 173 174 175 176 177
                .iter()
                .map(|f| {
                    let feature = &*f.as_str();
                    format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature))
                })
        )
178 179 180 181 182
        .collect::<Vec<String>>()
        .join(",");

    if !features.is_empty() {
        let val = CString::new(features).unwrap();
183 184 185 186
        llvm::AddFunctionAttrStringValue(
            llfn, llvm::AttributePlace::Function,
            cstr("target-features\0"), &val);
    }
187 188 189 190 191 192 193 194 195 196 197 198 199 200

    // Note that currently the `wasm-import-module` doesn't do anything, but
    // eventually LLVM 7 should read this and ferry the appropriate import
    // module to the output file.
    if cx.tcx.sess.target.target.arch == "wasm32" {
        if let Some(module) = wasm_import_module(cx.tcx, id) {
            llvm::AddFunctionAttrStringValue(
                llfn,
                llvm::AttributePlace::Function,
                cstr("wasm-import-module\0"),
                &module,
            );
        }
    }
201 202 203 204
}

fn cstr(s: &'static str) -> &CStr {
    CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string")
205
}
206 207 208 209

pub fn provide(providers: &mut Providers) {
    providers.target_features_whitelist = |tcx, cnum| {
        assert_eq!(cnum, LOCAL_CRATE);
210 211 212 213
        if tcx.sess.opts.actually_rustdoc {
            // rustdoc needs to be able to document functions that use all the features, so
            // whitelist them all
            Lrc::new(llvm_util::all_known_features()
214
                .map(|(a, b)| (a.to_string(), b.map(|s| s.to_string())))
215 216 217 218
                .collect())
        } else {
            Lrc::new(llvm_util::target_feature_whitelist(tcx.sess)
                .iter()
219
                .map(|&(a, b)| (a.to_string(), b.map(|s| s.to_string())))
220 221
                .collect())
        }
222
    };
223

224
    provide_extern(providers);
225 226
}

227 228
pub fn provide_extern(providers: &mut Providers) {
    providers.wasm_import_module_map = |tcx, cnum| {
229 230 231 232 233 234 235 236 237 238 239
        // Build up a map from DefId to a `NativeLibrary` structure, where
        // `NativeLibrary` internally contains information about
        // `#[link(wasm_import_module = "...")]` for example.
        let native_libs = tcx.native_libraries(cnum);
        let mut def_id_to_native_lib = FxHashMap();
        for lib in native_libs.iter() {
            if let Some(id) = lib.foreign_module {
                def_id_to_native_lib.insert(id, lib);
            }
        }

240 241
        let mut ret = FxHashMap();
        for lib in tcx.foreign_modules(cnum).iter() {
242 243 244
            let module = def_id_to_native_lib
                .get(&lib.def_id)
                .and_then(|s| s.wasm_import_module);
245 246 247 248 249 250 251 252 253 254 255
            let module = match module {
                Some(s) => s,
                None => continue,
            };
            for id in lib.foreign_items.iter() {
                assert_eq!(id.krate, cnum);
                ret.insert(*id, module.to_string());
            }
        }

        Lrc::new(ret)
256
    };
257 258 259 260 261 262 263
}

fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option<CString> {
    tcx.wasm_import_module_map(id.krate)
        .get(&id)
        .map(|s| CString::new(&s[..]).unwrap())
}