Skip to main content

rustc_target/callconv/
x86.rs

1use rustc_abi::{
2    AddressSpace, Align, BackendRepr, Float, HasDataLayout, Primitive, Reg, RegKind, TyAndLayout,
3};
4
5use crate::callconv::{ArgAttribute, FnAbi, PassMode, TyAbiInterface};
6use crate::spec::{HasTargetSpec, RustcAbi};
7
8#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for Flavor {
    #[inline]
    fn eq(&self, other: &Flavor) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq)]
9pub(crate) enum Flavor {
10    General,
11    FastcallOrVectorcall,
12}
13
14pub(crate) struct X86Options {
15    pub flavor: Flavor,
16    pub regparm: Option<u32>,
17    pub reg_struct_return: bool,
18}
19
20pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options)
21where
22    Ty: TyAbiInterface<'a, C> + Copy,
23    C: HasDataLayout + HasTargetSpec,
24{
25    if !fn_abi.ret.is_ignore() {
26        if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() {
27            // Returning a structure. Most often, this will use
28            // a hidden first argument. On some platforms, though,
29            // small structs are returned as integers.
30            //
31            // Some links:
32            // https://www.angelcode.com/dev/callconv/callconv.html
33            // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
34            let t = cx.target_spec();
35            if let Some(Float::F16) = fn_abi.ret.layout.complex_float(cx) {
36                // `_Complex _Float16` is returned as `<2 x half>`.
37                let kind = RegKind::Vector { hint_vector_elem: Primitive::Float(Float::F16) };
38                fn_abi.ret.cast_to(Reg { kind, size: fn_abi.ret.layout.size });
39            } else if t.abi_return_struct_as_int
40                || opts.reg_struct_return
41                || fn_abi.ret.layout.is_complex_number(cx)
42            {
43                // According to Clang, everyone but MSVC returns single-element
44                // float aggregates directly in a floating-point register.
45                if fn_abi.ret.layout.is_single_fp_element(cx) {
46                    match fn_abi.ret.layout.size.bytes() {
47                        4 => fn_abi.ret.cast_to(Reg::f32()),
48                        8 => fn_abi.ret.cast_to(Reg::f64()),
49                        _ => fn_abi.ret.make_indirect(),
50                    }
51                } else {
52                    match fn_abi.ret.layout.size.bytes() {
53                        1 => fn_abi.ret.cast_to(Reg::i8()),
54                        2 => fn_abi.ret.cast_to(Reg::i16()),
55                        4 => fn_abi.ret.cast_to(Reg::i32()),
56                        8 => fn_abi.ret.cast_to(Reg::i64()),
57                        _ => fn_abi.ret.make_indirect(),
58                    }
59                }
60            } else {
61                fn_abi.ret.make_indirect();
62            }
63        } else {
64            fn_abi.ret.extend_integer_width_to(32);
65        }
66    }
67
68    for arg in fn_abi.args.iter_mut() {
69        if arg.is_ignore() || !arg.layout.is_sized() {
70            continue;
71        }
72
73        if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
74            arg.make_indirect();
75            continue;
76        }
77
78        let t = cx.target_spec();
79        let align_4 = Align::from_bytes(4).unwrap();
80        let align_16 = Align::from_bytes(16).unwrap();
81
82        if arg.layout.is_aggregate() {
83            // We need to compute the alignment of the `byval` argument. The rules can be found in
84            // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized
85            // here, they are:
86            //
87            // 1. If the natural alignment of the type is <= 4, the alignment is 4.
88            //
89            // 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment.
90            // This doesn't matter here because we only pass aggregates via `byval`, not vectors.
91            //
92            // 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector
93            // type is 16.
94            //
95            // 4. If none of these conditions are true, the alignment is 4.
96
97            fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
98            where
99                Ty: TyAbiInterface<'a, C> + Copy,
100            {
101                match layout.backend_repr {
102                    BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false,
103                    BackendRepr::SimdVector { .. } => true,
104                    BackendRepr::Memory { .. } => {
105                        for i in 0..layout.fields.count() {
106                            if contains_vector(cx, layout.field(cx, i)) {
107                                return true;
108                            }
109                        }
110                        false
111                    }
112                    BackendRepr::SimdScalableVector { .. } => {
113                        {
    ::core::panicking::panic_fmt(format_args!("scalable vectors are unsupported"));
}panic!("scalable vectors are unsupported")
114                    }
115                }
116            }
117
118            let byval_align = if arg.layout.align.abi < align_4 {
119                // (1.)
120                align_4
121            } else if t.is_like_darwin && contains_vector(cx, arg.layout) {
122                // (3.)
123                align_16
124            } else {
125                // (4.)
126                align_4
127            };
128
129            arg.pass_by_stack_offset(Some(byval_align));
130        } else {
131            arg.extend_integer_width_to(32);
132        }
133    }
134
135    fill_inregs(cx, fn_abi, opts, false);
136}
137
138pub(crate) fn fill_inregs<'a, Ty, C>(
139    cx: &C,
140    fn_abi: &mut FnAbi<'a, Ty>,
141    opts: X86Options,
142    rust_abi: bool,
143) where
144    Ty: TyAbiInterface<'a, C> + Copy,
145{
146    if opts.flavor != Flavor::FastcallOrVectorcall && opts.regparm.is_none_or(|x| x == 0) {
147        return;
148    }
149    // Mark arguments as InReg like clang does it,
150    // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
151
152    // Clang reference: lib/CodeGen/TargetInfo.cpp
153    // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs()
154
155    // IsSoftFloatABI is only set to true on ARM platforms,
156    // which in turn can't be x86?
157
158    // 2 for fastcall/vectorcall, regparm limited by 3 otherwise
159    let mut free_regs = opts.regparm.unwrap_or(2).into();
160
161    // For types generating PassMode::Cast, InRegs will not be set.
162    // Maybe, this is a FIXME
163    let has_casts = fn_abi.args.iter().any(|arg| #[allow(non_exhaustive_omitted_patterns)] match arg.mode {
    PassMode::Cast { .. } => true,
    _ => false,
}matches!(arg.mode, PassMode::Cast { .. }));
164    if has_casts && rust_abi {
165        return;
166    }
167
168    for arg in fn_abi.args.iter_mut() {
169        let attrs = match arg.mode {
170            PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
171                continue;
172            }
173            PassMode::Direct(ref mut attrs) => attrs,
174            PassMode::Pair(..)
175            | PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ }
176            | PassMode::Cast { .. } => {
177                {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("x86 shouldn\'t be passing arguments by {0:?}",
                arg.mode)));
}unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
178            }
179        };
180
181        // At this point we know this must be a primitive of sorts.
182        let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
183        {
    match (&unit.size, &arg.layout.size) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(unit.size, arg.layout.size);
184        if #[allow(non_exhaustive_omitted_patterns)] match unit.kind {
    RegKind::Float | RegKind::Vector { .. } => true,
    _ => false,
}matches!(unit.kind, RegKind::Float | RegKind::Vector { .. }) {
185            continue;
186        }
187
188        let size_in_regs = arg.layout.size.bits().div_ceil(32);
189
190        if size_in_regs == 0 {
191            continue;
192        }
193
194        if size_in_regs > free_regs {
195            break;
196        }
197
198        free_regs -= size_in_regs;
199
200        if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
201            attrs.set(ArgAttribute::InReg);
202        }
203
204        if free_regs == 0 {
205            break;
206        }
207    }
208}
209
210pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
211where
212    Ty: TyAbiInterface<'a, C> + Copy,
213    C: HasDataLayout + HasTargetSpec,
214{
215    // Avoid returning floats in x87 registers on x86 as loading and storing from x87
216    // registers will quiet signalling NaNs. Also avoid using SSE registers since they
217    // are not always available (depending on target features).
218    if !fn_abi.ret.is_ignore() {
219        let has_float = match fn_abi.ret.layout.backend_repr {
220            BackendRepr::Scalar(s) => #[allow(non_exhaustive_omitted_patterns)] match s.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s.primitive(), Primitive::Float(_)),
221            BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => {
222                #[allow(non_exhaustive_omitted_patterns)] match s1.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s1.primitive(), Primitive::Float(_))
223                    || #[allow(non_exhaustive_omitted_patterns)] match s2.primitive() {
    Primitive::Float(_) => true,
    _ => false,
}matches!(s2.primitive(), Primitive::Float(_))
224            }
225            _ => false, // anyway not passed via registers on x86
226        };
227        if has_float {
228            if cx.target_spec().rustc_abi == Some(RustcAbi::X86Sse2)
229                && fn_abi.ret.layout.backend_repr.is_scalar()
230                && fn_abi.ret.layout.size.bits() <= 128
231            {
232                // This is a single scalar that fits into an SSE register, and the target uses the
233                // SSE ABI. We prefer this over integer registers as float scalars need to be in SSE
234                // registers for float operations, so that's the best place to pass them around.
235                fn_abi.ret.cast_to(Reg::opaque_vector(fn_abi.ret.layout.size));
236            } else if fn_abi.ret.layout.size <= Primitive::Pointer(AddressSpace::ZERO).size(cx) {
237                // Same size or smaller than pointer, return in an integer register.
238                fn_abi.ret.cast_to(Reg { kind: RegKind::Integer, size: fn_abi.ret.layout.size });
239            } else {
240                // Larger than a pointer, return indirectly.
241                fn_abi.ret.make_indirect();
242            }
243            return;
244        }
245    }
246}