summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_gcc/src/attributes.rs
blob: 6159971cfaa8d2a5dd0fd6bbc10b73b6abbb3834 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#[cfg(feature="master")]
use gccjit::FnAttribute;
use gccjit::Function;
use rustc_attr::InstructionSetAttr;
#[cfg(feature="master")]
use rustc_attr::InlineAttr;
use rustc_middle::ty;
#[cfg(feature="master")]
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_span::symbol::sym;

use crate::{context::CodegenCx, errors::TiedTargetFeatures};
use crate::gcc_util::{check_tied_features, to_gcc_features};

/// Get GCC attribute for the provided inline heuristic.
#[cfg(feature="master")]
#[inline]
fn inline_attr<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, inline: InlineAttr) -> Option<FnAttribute<'gcc>> {
    match inline {
        InlineAttr::Hint => Some(FnAttribute::Inline),
        InlineAttr::Always => Some(FnAttribute::AlwaysInline),
        InlineAttr::Never => {
            if cx.sess().target.arch != "amdgpu" {
                Some(FnAttribute::NoInline)
            } else {
                None
            }
        }
        InlineAttr::None => None,
    }
}

/// Composite function which sets GCC attributes for function depending on its AST (`#[attribute]`)
/// attributes.
pub fn from_fn_attrs<'gcc, 'tcx>(
    cx: &CodegenCx<'gcc, 'tcx>,
    #[cfg_attr(not(feature="master"), allow(unused_variables))]
    func: Function<'gcc>,
    instance: ty::Instance<'tcx>,
) {
    let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());

    #[cfg(feature="master")]
    {
        let inline =
            if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
                InlineAttr::Never
            }
            else if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) {
                InlineAttr::Hint
            }
            else {
                codegen_fn_attrs.inline
            };
        if let Some(attr) = inline_attr(cx, inline) {
            if let FnAttribute::AlwaysInline = attr {
                func.add_attribute(FnAttribute::Inline);
            }
            func.add_attribute(attr);
        }

        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
            func.add_attribute(FnAttribute::Cold);
        }
        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
            func.add_attribute(FnAttribute::ReturnsTwice);
        }
        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
            func.add_attribute(FnAttribute::Pure);
        }
        if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
            func.add_attribute(FnAttribute::Const);
        }
    }

    let function_features =
        codegen_fn_attrs.target_features.iter().map(|features| features.as_str()).collect::<Vec<&str>>();

    if let Some(features) = check_tied_features(cx.tcx.sess, &function_features.iter().map(|features| (*features, true)).collect()) {
        let span = cx.tcx
            .get_attr(instance.def_id(), sym::target_feature)
            .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span);
        cx.tcx.sess.create_err(TiedTargetFeatures {
            features: features.join(", "),
            span,
        })
            .emit();
        return;
    }

    let mut function_features = function_features
        .iter()
        .flat_map(|feat| to_gcc_features(cx.tcx.sess, feat).into_iter())
        .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x {
            InstructionSetAttr::ArmA32 => "-thumb-mode", // TODO(antoyo): support removing feature.
            InstructionSetAttr::ArmT32 => "thumb-mode",
        }))
        .collect::<Vec<_>>();

    // TODO(antoyo): cg_llvm adds global features to each function so that LTO keep them.
    // Check if GCC requires the same.
    let mut global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
    function_features.extend(&mut global_features);
    let target_features = function_features
        .iter()
        .filter_map(|feature| {
            // FIXME(antoyo): for some reasons, disabling SSE results in the following error when
            // compiling Rust for Linux:
            // SSE register return with SSE disabled
            // TODO(antoyo): support soft-float and retpoline-external-thunk.
            if feature.contains("soft-float") || feature.contains("retpoline-external-thunk") || *feature == "-sse" {
                return None;
            }

            if feature.starts_with('-') {
                Some(format!("no{}", feature))
            }
            else if feature.starts_with('+') {
                Some(feature[1..].to_string())
            }
            else {
                Some(feature.to_string())
            }
        })
        .collect::<Vec<_>>()
        .join(",");
    if !target_features.is_empty() {
        #[cfg(feature="master")]
        func.add_attribute(FnAttribute::Target(&target_features));
    }
}